django - 如何交叉检查 ModelAdmin 及其内联?

发布于 2024-08-30 19:13:59 字数 908 浏览 5 评论 0原文

我有两个模型(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 的 subject_child 字段不得引用 subject_parent 与其 subject_parent 所做的任何主题。

因此,如果我在两个模型的管理页面上选择相同的主题(在 subject_parent 和 subject_child 中),我该如何验证这一点? 如果只有一个字段发生变化,您可以根据数据库验证它,但如果两者都发生变化(subject_parent 和 subject_child)怎么办? 如何在保存之前同时验证这两个表单?

I have two models (ModelParent and ModelChild) with same m2m fields on Subject model.
ModelChild has a foreign key on ModelParent and ModelChild is defined as inline for ModelParent on admin page.

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

I have one important restriction though, ModelChild's subjects_child field must not reference any subject that subject_parent does with its subjects_parent.

So, if I select the same Subject (in subject_parent and subject_child) on Admin page for both models, how can I validate this?
If only one field changes you validate it against the db, but what if both change (subject_parent and subject_child)?
How can I validate both forms together before saving?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

煞人兵器 2024-09-06 19:13:59

我从 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)

该解决方案有点黑客,但它有效:)。这些错误的显示与常规 ModelForm 和 ModelAdmin 类相同。 Django 1.2(应该很快就会发布)应该有模型验证,所以我希望这个问题能够得到更好的解决。

I have inherited a new class named ModelAdminWithInline from admin.ModelAdmin and modified methods add_view(...) and change_view(...) to call function is_cross_valid(self, form, formsets), where you can validate all the forms together.
Both functions had:

#...
if all_valid(formsets) and form_validated:
#...

changed to:

#...
formsets_validated = all_valid(formsets)
cross_validated = self.is_cross_valid(form, formsets)
if formsets_validated and form_validated and cross_validated:
#...

The new function is_cross_valid(...) is defined like this:

def is_cross_valid(self, form, formsets):
  return True

so the new class should work exactly the same as ModelAdmin if you don't change is_cross_valid(...) function.

Now my admin.py looks like this:

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

The solution is a little bit hackish but it works :). The errors show up the same as with regular ModelForm and ModelAdmin classes. Django 1.2 (which should be released shortly) should have model validation, so I hope that then this problem could be solved more nicely.

分開簡單 2024-09-06 19:13:59

管理类没有 clean() 方法。他们的形式确实如此。每个管理类都有一个名为 form 的参数。您只需扩展默认表单(这是普通的 ModelAdmin 表单),实现 clean() 方法并将该表单添加到管理类中。
例子:

class SomeForm(ModelForm):
  #some code
  def clean(self):
   #some code
class SomeAdminClass(ModelAdmin):
 #some code
 form = SomeForm
 #more code

The admin classes do not have the clean() method. Their forms do. Each admin class has a parameter called form. You simply extend the default form (it's the normal ModelAdmin form), implement the clean() method and add the form to the admin class.
Example:

class SomeForm(ModelForm):
  #some code
  def clean(self):
   #some code
class SomeAdminClass(ModelAdmin):
 #some code
 form = SomeForm
 #more code
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文