Django - clean_field() is not being called for updates on admin inline form

django, django-admin, django-forms

Solution

Validation occurs on `ModelForm` objects, not `ModelAdmin` objects. If you want to override any clean methods then you have to create your own `ModelForm` descendent for each required model.

In your example, the `PrimaryInline` class does not specify a `form`. If not specified, the form used will be a `ModelForm`, which doesn't have any of your custom clean methods.

Try this:

class PrimaryInline(admin.StackedInline):
    # ... existing code ...
    form = PrimaryAdminForm

This will now use your custom `PrimaryAdminForm` with associated `clean()` methods.

Problem

I can not figure out why a clean_field() method is not being called for an inline form that is updated on an admin view. The code I have seems straight-forward (see synopsis below). When I modify the Primary form through admin interface (`http://admin/..../primary/1/`), as expected, I see: - Admin.PrimaryAdminForm.clean_myfield() called - Admin.PrimaryAdminForm.clean() called - Model.Primary.clean() called However, when I modify the Primary as seen as an inline on the Admin view of Membership (`http://admin/..../membership/1/`), I only see: - Model.Primary.clean() called I have tried placing the "def clean_myfield(self):" method in the following locations but can not see it get executed from the Membership inlined Primary form: - Model.Primary.clean_myfield - Admin.PrimaryAdmin.clean_myfield - Admin.PrimaryAdminForm.clean_myfield - Admin.PrimaryAdminInline.clean_myfield Is there somewhere else this clean_myfield code should be placed? I have read (and reread) the Django docs on [forms and field validation][docs.djangoproject.com/en/dev/ref/forms/validation/#form-and-field-validation] which gives a great coverage, but there's nothing on inline validation. I've also read docs.djangoproject.com/en/dev/ref/contrib/admin/#adding-custom-validation-to-the-admin, but no help for inline specific validation. Is there other documentation on this? ---> Answered by Austin provided a doc reference to: "If not specified" (see his link) , which implies the answer. I added a request to improve the documents on this topic. After further experimenting I found a workaround by putting code in the Model.Primary.clean() method: ``` def clean(self): data = self.myfield data += "_extra" # not actual cleaning code self.myfield = data ``` So the question remains: Why is Model.clean() seem to be the only place to put admin inline form validation and not in a clean_myfield(self) method? ---> Answered by Austin. I needed add `form = PrimaryAdminForm` to PrimaryInline. With this addition, PrimaryAdminForm.clean_myfield(self) is called when PrimaryInline myfield is updated on Membership form. Code ordering was updated due to the added form reference. Code synopsis: No forms.py file -- all models are updated through admin interface models.py: ``` class Membership(models.Model): name = models.CharField( max_length=NAME_LENGTH, null=True, blank=True, verbose_name="Membership Name Tag", help_text="Name of membership" ) class Primary(models.Model): user = models.OneToOneField(User, verbose_name="User Name") membership = models.OneToOneField(Membership, verbose_name="Membership Name") myfield = models.CharField("My Field", max_length=20, null=True, blank=True) # clean method altered as in Update comment # Why must this be here? Why not in clean_myfield(self) def clean(self): data = self.myfield data += "_extra" # not actual cleaning code self.myfield = data ``` admin.py: ``` class MembershipAdminForm(ModelForm): class Meta: model = Membership class PrimaryAdminForm(ModelForm): class Meta: model = Primary def clean_myfield(self): data = self.cleaned_data['myfield'] data += "_extra" # not actual cleaning code return unicode(data) def clean(self): cleaned_data = super(PrimaryAdminForm, self).clean() # not actual cleaning code return cleaned_data # EDIT2: Moved PrimaryInline so it's defined after PrimaryAdminForm class PrimaryInline(admin.StackedInline): model = Primary form = PrimaryAdminForm #EDIT2 as recommended by Austin raw_id_fields = ['user'] verbose_name_plural = 'Primary Member' fieldsets = ((None, {'classes': ('mbship', ), 'fields': ('user', 'myfield')}), ) class MembershipAdmin(admin.ModelAdmin): form = MembershipAdminForm # inlines inlines = [PrimaryInline, ] fieldsets = ((None, {'classes': ('mbship',), 'fields': ('name'), }), ) class PrimaryAdmin(admin.ModelAdmin): form = PrimaryAdminForm list_display = ('__unicode__', 'user', 'status', 'date_used' ) search_fields = ['user__first_name', 'user__last_name', 'user__email'] fieldsets = ((None, {'classes': ('mbship',), 'fields': ('user', 'membership', 'myfield'), }), ) def clean_myfield(self): data = self.cleaned_data['myfield'] data += "_extra" # not actual cleaning code return unicode(data) ```

Original source