Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

does django models offer something similar to forms' clean_<fieldname>()?

I am trying to move all business-logic-related validations to models, instead of leaving them in forms. But here I have a tricky situation, for which I like to consult with the SO community.

In my SignupForm (a model form), I have the following field-specific validation to make sure the input email does not exist already.

def clean_email(self):
    email = self.cleaned_data['email']

    if ExtendedUser.objects.filter(email=email).exists():
        raise ValidationError('This email address already exists.')
    return email

If I were to move this validation to the models, according to the official doc, I would put it in clean() of the corresponding model, ExtendedUser. But the doc also mentions the following:

Any ValidationError exceptions raised by Model.clean() will be stored in a special key error dictionary key, NON_FIELD_ERRORS, that is used for errors that are tied to the entire model instead of to a specific field

That means, with clean(), I cannot associate the errors raised from it with specific fields. I was wondering if models offer something similar to forms' clean_<fieldname>(). If not, where would you put this validation logic and why?

like image 368
tamakisquare Avatar asked Oct 08 '22 08:10

tamakisquare


2 Answers

You could convert your clean method into a validator and include it when you declare the field.

Another option is to subclass the model field and override its clean method.

However there is no direct equivalent of defining clean_<field name> methods as you can do for forms. You can't even assign errors to individual fields, as you can do for forms

like image 61
Alasdair Avatar answered Oct 15 '22 21:10

Alasdair


As stated in the comment I believe you should handle this validation at the modelform level. If you still feel like it would be better to do it closer to the model, and since they can't be changed, I would advise a change directly at the db level:

ALTER TABLE auth_user ADD UNIQUE (email)

Which is the poor way to add the unique=True constraint to the User model without monkey patching auth.

As requested, I think that a good way to go about customizing different forms should be done by inheriting from a base modelform. A good example of this is found in django-registration. The only difference is that instead of the parent form inheriting from forms.Form you would make it a modelForm:

class MyBaseModelForm(ModelForm):
    class Meta:
        model = MyModel

You could then inherit from it and make different forms from this base model:

class OtherFormWithCustomClean(MyBaseModelForm):
    def clean_email(self):
       email = self.cleaned_data['email']
       if ExtendedUser.objects.filter(email=email).exists():
          raise ValidationError('This email address already exists.')
    return email
like image 20
vascop Avatar answered Oct 15 '22 20:10

vascop