我有以下管理员设置,以便我可以同时添加/编辑用户及其个人资料。
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)
问题是我在添加用户时需要配置文件内联表单中的两个字段。除非输入输入,否则内联表单不会验证。反正是否需要内联,以便它不能留空?
答案 0 :(得分:30)
我接受了卡尔的建议并做了一个更好的实施,然后是我在回答他的评论时提到的黑客。这是我的解决方案:
来自我的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 inline formset验证。因此,如果未在内联表单中输入所需信息,则在配置文件表单中有必填字段,它将验证并失败。
答案 1 :(得分:21)
现在使用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)
答案 2 :(得分:9)
你可能会这样做,但是你必须在formset / inline代码中弄清楚。
首先,我认为你希望在这种情况下总是在formset中有一个表单,并且永远不会超过一个,所以你需要设置max_num = 1和extra在你的ProfileInline中= 1。
您的核心问题是BaseFormSet._construct_form passes empty_permitted=True到表单集中的每个“额外”(即空)表单。此参数告诉表单如果未更改则绕过验证。您只需找到一种方法为表单设置empty_permitted = False。
您可以在内联中use your own BaseInlineFormset subclass,这样可能有所帮助。注意到_construct_form需要** kwargs并允许它覆盖传递给各个Form实例的kwargs,你可以覆盖Formset子类中的_construct_forms并让它在每次调用_construct_form时传递empty_permitted = False。缺点是您依赖于内部API(并且您必须重写_construct_forms)。
或者,您可以尝试覆盖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
四处游玩,看看你能做些什么!
答案 3 :(得分:7)
最简单,最自然的方法是通过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中使用。)
答案 4 :(得分:0)
您需要内联设置 min_num ,并在表单集中设置 validate_min 。
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
答案 5 :(得分:0)
自Django 3+起 这很简单,只要您喜欢:
class EmployeeAddressMap(admin.StackedInline):
model = EmployeeAddress
min_num = 1
max_num = 1
can_delete = False #specified that this cannnot be removed
快乐编码