Django中的内联表单验证
我想在管理员变更表单中强制设置一个完整的内联表单集。因此,在我当前的场景中,当我在发票表单上点击save(在Admin中)时,内联订单表单为空。我想阻止人们创建没有关联订单的发票 有人知道一个简单的方法吗Django中的内联表单验证,django,django-forms,Django,Django Forms,我想在管理员变更表单中强制设置一个完整的内联表单集。因此,在我当前的场景中,当我在发票表单上点击save(在Admin中)时,内联订单表单为空。我想阻止人们创建没有关联订单的发票 有人知道一个简单的方法吗 在这种情况下,模型字段上的正常验证(如(required=True)似乎不起作用。最好的方法是定义一个自定义表单集,使用一个干净的方法验证至少存在一个发票订单 class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
在这种情况下,模型字段上的正常验证(如(
required=True
)似乎不起作用。最好的方法是定义一个自定义表单集,使用一个干净的方法验证至少存在一个发票订单
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def clean(self):
# get forms that actually have valid data
count = 0
for form in self.forms:
try:
if form.cleaned_data:
count += 1
except AttributeError:
# annoyingly, if a subform is invalid Django explicity raises
# an AttributeError for cleaned_data
pass
if count < 1:
raise forms.ValidationError('You must have at least one order')
class InvoiceOrderInline(admin.StackedInline):
formset = InvoiceOrderInlineFormset
class InvoiceAdmin(admin.ModelAdmin):
inlines = [InvoiceOrderInline]
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def清洁(自清洁):
#获取实际具有有效数据的表单
计数=0
对于self.forms中的表单:
尝试:
如果form.u数据:
计数+=1
除属性错误外:
#令人烦恼的是,如果子窗体无效,Django会显式地引发
#已清除数据的AttributeError
通过
如果计数小于1:
raise forms.ValidationError('您必须至少有一个订单')
类InvoiceOrderInline(admin.StackedInline):
formset=InvoiceOrderInlineFormset
类InvoiceAdmin(admin.ModelAdmin):
inlines=[InvoiceOrderInline]
Daniel的回答很好,在一个项目中对我很有效,但后来我意识到,由于Django表单的工作方式,如果您在保存时使用can_delete并选中delete框,则可以验证任何订单(在本例中)
我花了一段时间试图找出如何防止这种情况发生。第一种情况很简单-不要在计数中包含将被删除的表单。第二种情况比较棘手……如果选中了所有的删除框,则不会调用clean
不幸的是,代码并不十分简单。clean
方法是从full\u clean
调用的,在访问error
属性时调用该方法。删除子窗体时不会访问此属性,因此永远不会调用full\u clean
。我不是Django专家,所以这可能是一种可怕的方法,但似乎有效
下面是修改后的类:
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def is_valid(self):
return super(InvoiceOrderInlineFormset, self).is_valid() and \
not any([bool(e) for e in self.errors])
def clean(self):
# get forms that actually have valid data
count = 0
for form in self.forms:
try:
if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
count += 1
except AttributeError:
# annoyingly, if a subform is invalid Django explicity raises
# an AttributeError for cleaned_data
pass
if count < 1:
raise forms.ValidationError('You must have at least one order')
class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet):
def有效(自):
return super(InvoiceOrderInlineFormset,self)。有效吗?()和\
没有任何([bool(e)表示self.errors中的e])
def清洁(自清洁):
#获取实际具有有效数据的表单
计数=0
对于self.forms中的表单:
尝试:
如果form.cleaned_数据而不是form.cleaned_数据.get('DELETE',False):
计数+=1
除属性错误外:
#令人烦恼的是,如果子窗体无效,Django会显式地引发
#已清除数据的AttributeError
通过
如果计数小于1:
raise forms.ValidationError('您必须至少有一个订单')
类强制InLineFormSet(BaseInlineFormSet):
def有效(自):
返回super(MandatoryInlineFormSet,self)。_是否有效()和\
没有任何([bool(e)表示self.errors中的e])
def清洁(自清洁):
#获取实际具有有效数据的表单
计数=0
对于self.forms中的表单:
尝试:
如果form.cleaned_数据而不是form.cleaned_数据.get('DELETE',False):
计数+=1
除属性错误外:
#令人烦恼的是,如果子窗体无效,Django会显式地引发
#已清除数据的AttributeError
通过
如果计数小于1:
raise forms.ValidationError('您必须至少拥有其中一个')
类mandatorytablerinline(admin.tablerinline):
formset=MandatoryInlineFormSet
类mandatoryStackedLine(admin.stackedLine):
formset=MandatoryInlineFormSet
类CommentInlineFormSet(MandatoryInlineFormSet):
def清洁_等级(自身、表格):
"""
额定值必须为0..5乘0.5的增量
"""
额定值=浮动(表.数据['额定值])
如果评级<0或评级>5:
raise ValidationError(“评级必须在0-5之间”)
如果(额定值/0.5)!=整数(额定值/0.5):
raise ValidationError(“评级必须有.0或.5小数点”)
def清洁(自清洁):
super(CommentInlineFormSet,self).clean()
对于self.forms中的表单:
自洁度(表格)
类CommentInline(MandatoryTablerinline):
formset=CommentInlineFormSet
模型=注释
额外=1
@Daniel Roseman解决方案很好,但我做了一些修改,减少了一些代码来实现这一点
class RequiredFormSet(forms.models.BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
self.forms[0].empty_permitted = False
class InvoiceOrderInline(admin.StackedInline):
model = InvoiceOrder
formset = RequiredFormSet
class InvoiceAdmin(admin.ModelAdmin):
inlines = [InvoiceOrderInline]
试试这个也行:)情况有所好转,但仍需要一些解决办法。Django现在提供了
validate\u min
和min\u num
属性,如果在表单集实例化期间min\u num
取自Inline
,则validate\u min
只能作为init formset参数传递。因此,我的解决方案如下所示:
class MinValidatedInlineMixIn:
验证_min=True
def get_formset(self,*args,**kwargs):
return super().get_formset(validate_min=self.validate_min,*args,**kwargs)
类InvoiceOrderInline(minValidateInLineMixin,admin.StackedInline):
型号=发票订单
最小数量=1
验证_min=True
类InvoiceAdmin(admin.ModelAdmin):
inlines=[InvoiceOrderInline]
我发现,如果选中删除框,则可以使用0个订单进行验证。请参阅我的答案,了解解决该问题的修订课程。非常感谢您的修复(以及Dan的增强)。作为对其他人的可能提示,我制作了一个“类MandatoryInlineFormSet(BaseInlineFormSet)”,然后从中派生出InvoiceAdminFormSet。在我的库存中
class RequiredFormSet(forms.models.BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
self.forms[0].empty_permitted = False
class InvoiceOrderInline(admin.StackedInline):
model = InvoiceOrder
formset = RequiredFormSet
class InvoiceAdmin(admin.ModelAdmin):
inlines = [InvoiceOrderInline]