django - How to cross check ModelAdmin and its inlines?

后端 未结 2 1136
猫巷女王i
猫巷女王i 2020-12-30 14:43

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 Mo

相关标签:
2条回答
  • 2020-12-30 15:13

    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
    
    0 讨论(0)
  • 2020-12-30 15:17

    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.

    0 讨论(0)
提交回复
热议问题