代码之家  ›  专栏  ›  技术社区  ›  AJ.

如何在Django管理中要求内联?

  •  24
  • AJ.  · 技术社区  · 17 年前

    我有以下管理设置,这样我可以同时添加/编辑用户及其配置文件。

    class ProfileInline(admin.StackedInline):
        """
        Allows profile to be added when creating user
        """
        model = Profile
    
    
    class UserProfileAdmin(admin.ModelAdmin):
        """
        Options for the admin interface
        """
        inlines = [ProfileInline]
        list_display = ['edit_obj', 'name', 'username', 'email', 'is_active',
            'last_login', 'delete_obj']
        list_display_links = ['username']
        list_filter = ['is_active']
        fieldsets = (
            (None, {
                'fields': ('first_name', 'last_name', 'email', 'username',
                    'is_active', 'is_superuser')}),
            )
        ordering = ['last_name', 'first_name']
        search_fields = ['first_name', 'last_name']
    
    admin.site.register(User, UserProfileAdmin)
    

    问题是,在添加用户时,需要配置文件内联表单中的两个字段。除非输入了输入,否则内联表单不会验证。是否仍要使内联成为必需的,以便不能保留为空?

    5 回复  |  直到 7 年前
        1
  •  30
  •   L42y    14 年前

    我接受了卡尔的建议,并做了一个更好的实现,然后我在评论中提到的那个黑客式的,对他的回答。这是我的解决方案:

    从my forms.py:

    from django.forms.models import BaseInlineFormSet
    
    
    class RequiredInlineFormSet(BaseInlineFormSet):
        """
        Generates an inline formset that is required
        """
    
        def _construct_form(self, i, **kwargs):
            """
            Override the method to change the form attribute empty_permitted
            """
            form = super(RequiredInlineFormSet, self)._construct_form(i, **kwargs)
            form.empty_permitted = False
            return form
    

    还有行政人员。

    class ProfileInline(admin.StackedInline):
        """
        Allows profile to be added when creating user
        """
        model = Profile
        extra = 1
        max_num = 1
        formset = RequiredInlineFormSet
    
    
    class UserProfileAdmin(admin.ModelAdmin):
        """
        Options for the admin interface
        """
        inlines = [ProfileInline]
        list_display = ['edit_obj', 'name', 'username', 'email', 'is_active',
            'last_login', 'delete_obj']
        list_display_links = ['username']
        list_filter = ['is_active']
        fieldsets = (
            (None, {
                'fields': ('first_name', 'last_name', 'email', 'username',
                    'is_active', 'is_superuser')}),
            (('Groups'), {'fields': ('groups', )}),
        )
        ordering = ['last_name', 'first_name']
        search_fields = ['first_name', 'last_name']
    
    
    admin.site.register(User, UserProfileAdmin)
    

    这正是我想要的,它使配置文件内联表单集生效。因此,由于概要文件表单中存在必需的字段,因此如果所需的信息没有输入到内联表单中,那么它将验证并失败。

        2
  •  20
  •   quick    11 年前

    现在,对于django 1.7,可以使用参数 min_num . 你不需要上课 RequiredInlineFormSet 不再。

    见 https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.min_num

    class ProfileInline(admin.StackedInline):
        """
        Allows profile to be added when creating user
        """
        model = Profile
        extra = 1
        max_num = 1
        min_num = 1 # new in Django 1.7
    
    
    class UserProfileAdmin(admin.ModelAdmin):
        """
        Options for the admin interface
        """
        inlines = [ProfileInline]
        ...
    
    
    admin.site.register(User, UserProfileAdmin)
    
        3
  •  9
  •   Carl Meyer    17 年前

    您可能可以这样做,但在formset/inline代码中,您必须将手弄脏。

    首先,我认为在这种情况下,您希望表单集中始终有一个表单,并且永远不会有多个表单,因此您将希望设置 max_num = 1和 extra =1在您的profileinline中。

    你的核心问题是 BaseFormSet._construct_form passes empty_permitted=True 表单集中的每个“额外”(即空)表单。此参数指示窗体在其不变时跳过验证。您只需要找到一种方法来为表单设置空的\u permitted=false。

    你可以 use your own BaseInlineFormset subclass 在您的内联中,这样可能会有所帮助。注意到“construct”表单接受**Kwargs并允许覆盖传递给各个表单实例的Kwargs,您可以覆盖Formset子类中的“construct”表单,并在每次调用“construct”表单时将其传递为空的“permitted=false”。缺点是您依赖于内部API(并且您必须重写构造表单)。

    或者,您可以尝试重写profileinline上的get_formset方法,调用父级的get_formset后,手动戳到返回的formset中的表单:

    def get_formset(self, request, obj=None, **kwargs):
        formset = super(ProfileInline, self).get_formset(request, obj, **kwargs)
        formset.forms[0].empty_permitted = False
        return formset
    

    四处游玩,看看你能做些什么!

        4
  •  7
  •   mrts    8 年前

    最简单和最自然的方法是通过FOMSET clean() :

    class RequireOneFormSet(forms.models.BaseInlineFormSet):
        def clean(self):
            super().clean()
            if not self.is_valid():
                return
            if not self.forms or not self.forms[0].cleaned_data:
                raise ValidationError('At least one {} required'
                                      .format(self.model._meta.verbose_name))
    
    class ProfileInline(admin.StackedInline):
        model = Profile
        formset =  RequireOneFormSet
    

    (灵感来自于 this Matthew Flanagan's snippet 以及Mitar在下面的评论,测试了在Django 1.11和2.0工作)。

        5
  •  0
  •   un1t    7 年前

    你需要设置 明尼姆 内联和 验证有效 在表格集中。

    https://docs.djangoproject.com/en/1.8/topics/forms/formsets/#validate-min

    class SomeInline(admin.TabularInline):
        ...
        min_num = 1
    
        def get_formset(self, request, obj=None, **kwargs):
            formset = super().get_formset(request, obj=None, **kwargs)
            formset.validate_min = True
            return formset