如何在Django Admin中要求内联?

29 投票
7 回答
17316 浏览
提问于 2025-04-15 13: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)

现在的问题是,在添加用户时,我需要个人资料中的两个字段是必填的。但是,在线表单在没有输入内容时不会进行验证。有没有办法让这个在线表单变成必填,这样就不能留空呢?

7 个回答

10

你可能可以做到这一点,但需要在表单集和内联代码上动点手脚。

首先,我觉得在这种情况下,你希望表单集中始终只有一个表单,而不会有多个。所以你需要在你的 ProfileInline 中设置 max_num=1 和 extra=1。

你的核心问题是 BaseFormSet._construct_form 会将 empty_permitted=True 传递给每个“额外的”(即空的)表单。这个参数的意思是,如果表单没有变化,就跳过验证。你只需要找到一种方法,将 empty_permitted 设置为 False。

你可以 在你的内联中使用自己的 BaseInlineFormset 子类,这可能会有帮助。注意到 _construct_form 接受 **kwargs 参数,并允许你覆盖传递给每个表单实例的 kwargs,你可以在你的 Formset 子类中重写 _construct_forms,并在每次调用 _construct_form 时传递 empty_permitted=False。这样做的缺点是你依赖于内部 API(而且你需要重写 _construct_forms)。

另外,你也可以尝试重写 ProfileInline 的 get_formset 方法,在调用父类的 get_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

试着动手看看能做成什么!

35

现在使用Django 1.7,你可以使用一个叫做 min_num 的参数了。你不再需要使用 RequiredInlineFormSet 这个类了。

详情请查看 这个链接

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)
34

我听了Carl的建议,做了一个比我在评论中提到的那个简陋实现要好得多的版本。下面是我的解决方案:

来自我的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

还有admin.py文件:

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)

这个实现完全符合我的需求,它让Profile的内联表单能够进行验证。所以,因为Profile表单中有必填字段,如果在内联表单中没有填写这些必填信息,它就会验证失败。

撰写回答