我有两个模型(ModelParent和ModelChild),主题模型上有相同的m2m字段。 ModelChild在ModelParent上有一个外键,ModelChild在管理页面上定义为ModelParent的内联。
### models.py ###
class Subject(Models.Model):
pass
class ModelParent(models.Model):
subjects_parent = ManyToManyField(Subject)
class ModelChild(models.Model):
parent = ForeignKey(ModelParent)
subjects_child = ManyToManyField(Subject)
### admin.py ###
class ModelChildInline(admin.TabularInline):
model = ModelChild
class ModelParentAdmin(admin.ModelAdmin):
inlines = [ModelChildInline]
admin.site.register(ModelParent, ModelParentAdmin)
我有一个重要的限制,ModelChild的subjects_child字段不得引用subject_parent与subject_parent一起使用的任何主题。
因此,如果我在两个模型的管理页面上选择相同的主题(在subject_parent和subject_child中),我该如何验证? 如果只有一个字段发生更改,则会根据数据库对其进行验证,但如果两者都发生更改(subject_parent和subject_child)会怎样? 如何在保存前同时验证两种表格?
答案 0 :(得分:5)
我从admin.ModelAdmin继承了一个名为ModelAdminWithInline的新类,并修改了方法add_view(...)和change_view(...)以调用函数is_cross_valid(self,form,formsets),您可以在其中验证所有表单一起。 这两个职能都有:
#...
if all_valid(formsets) and form_validated:
#...
更改为:
#...
formsets_validated = all_valid(formsets)
cross_validated = self.is_cross_valid(form, formsets)
if formsets_validated and form_validated and cross_validated:
#...
新函数is_cross_valid(...)的定义如下:
def is_cross_valid(self, form, formsets):
return True
因此,如果不更改is_cross_valid(...)函数,则新类应与ModelAdmin完全相同。
现在我的admin.py看起来像这样:
###admin.py###
class ModelAdminWithInline(admin.ModelAdmin):
def is_cross_valid(self, form, formsets):
return True
def add_view(self, request, form_url='', extra_context=None):
#modified code
def change_view(self, request, object_id, extra_context=None):
#modified code
class ModelChildInline(admin.TabularInline):
model = ModelChild
class ModelParentAdmin(ModelAdminWithInline):
inlines = [ModelChildInline]
def is_cross_valid(self, form, formsets):
#Do some cross validation on forms
#For example, here is my particular validation:
valid = True
if hasattr(form, 'cleaned_data'):
subjects_parent = form.cleaned_data.get("subjects_parent")
#You can access forms from formsets like this:
for formset in formsets:
for formset_form in formset.forms:
if hasattr(formset_form, 'cleaned_data'):
subjects_child = formset_form.cleaned_data.get("subjects_child")
delete_form = formset_form.cleaned_data.get("DELETE")
if subjects_child and (delete_form == False):
for subject in subjects_child:
if subject in subjects_parent:
valid = False
#From here you can still report errors like in regular forms:
if "subjects_child" in formset_form.cleaned_data.keys():
formset_form._errors["subjects_child"] = ErrorList([u"Subject %s is already selected in parent ModelParent" % subject])
del formset_form.cleaned_data["subjects_child"]
else:
formset_form._errors["subjects_child"] += ErrorList(u"Subject %s is already selected in parent ModelParent" % subject])
#return True on success or False otherwise.
return valid
admin.site.register(ModelParent, ModelParentAdmin)
解决方案有点hackish但它的工作原理:)。错误显示与常规ModelForm和ModelAdmin类相同。 Django 1.2(应该很快发布)应该有模型验证,所以我希望这个问题可以更好地解决。
答案 1 :(得分:2)
管理类没有clean()方法。他们的形式呢。每个管理类都有一个名为form的参数。您只需扩展默认表单(它是正常的ModelAdmin表单),实现clean()方法并将表单添加到管理类。 例如:
class SomeForm(ModelForm):
#some code
def clean(self):
#some code
class SomeAdminClass(ModelAdmin):
#some code
form = SomeForm
#more code