4

For the following I am using Python 2.7 / Django 1.5.

I am trying to refactor some production code which overrides Django form's clean() method with a custom validation procedure. It is repeated in several of the forms so we want to abstract it to an external function that we can just call within the forms.

Say I have the following,

# ... imports ...

class MyProperties(models.Model):
    label = models.CharField(max_length=100, blank=True, null=True, verbose_name=u'label'

    def __unicode__(self):
        return self.label

class MyForm(forms.ModelForm):
    choices = forms.ModelChoiceField(required=False, ...)

    class Meta:
        model = MyProperties

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)

    def clean(self):
        return my_custom_clean_function(self)

def my_custom_clean_function(form_instance):
    cleaned_data = super(type(form_instance), form_instance).clean() ## DOES NOT WORK

    # other validation routines
    return cleaned_data

Calling super from the my_custom_clean_function from outside the class leads to exceeding the maximum recursive depth (is just calls the clean() method in the form_instance class, which calls the custom function, etc...

Creating a temp instance of ModelForm and calling it's clean method does not seem to work, since it would have none of the fields.

Is there any way to do this idiomatically, or would I be better off calling the parent clean method() from inside the form class, and then passing that data to my custom method for further validation?

gos1
  • 360
  • 5
  • 10

1 Answers1

4

Your super(type(instance), instance).clean() works too; unless you are subclassing your forms further, at which point type(instance) would be the wrong object to pass in.

Alternatively use super() in the method and pass that result in:

class MyForm(forms.ModelForm):
    # ...

    def clean(self):
        return my_custom_clean_function(super(MyForm, form_instance).clean())

def my_custom_clean_function(cleaned_data):    
    # other validation routines
    return cleaned_data

Or you could just make it a mixin:

class SharedCleanupMixin:
    def clean(self):
        cleaned_data = super(SharedCleanupMixin, self).clean()
        # other validation routines
        return cleaned_data

and use that in your forms:

class MyForm(forms.ModelForm, SharedCleanupMixin):
    # ...
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thank you so much! I ended up going with the first alternative, I can't believe it didn't occur to me. Upon further inspection of the code, I think there was a further level of subclassing that I missed - very large codebase with no comments in it! - so maybe that's what was breaking my direct call to super. Thanks again! – gos1 Jul 01 '15 at 19:56