Django model and form validation is a somewhat complicated and confusing topic in Django app development. A developer needs to understand several basic concepts such as model, model field, form, model form, etc. to have a good understanding of validation. Most Django books and online tutorials do not have a good discussion on validation.
Django official documentation has detailed descriptions on validation. However, the contents are dispersed on several places. This post describes the materials I have read on this topic.
The validator official documentation page is a good starting point to study model and form validation.
The validator function is a callable that takes a value and raises a ValidationError if not validated. Here is an example from the page:
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_even(value):
if value % 2 != 0:
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
from django.db import models
class MyModel(models.Model):
even_field = models.IntegerField(validators=[validate_even])
The subsection how validators are run on the validator page has three links.
A model’s full_clean()
method performs model validation. The method calls
three other methods:
clean_fields()
methodclean()
method, as a wholevalidate_unique()
methodThe model save()
method does NOT call full_clean()
method automatically.
A programmer needs to call it manually to trigger model validation like
the below code.
try:
article.full_clean()
except ValidationError as e:
...
# handle the error
A stack overflow answer
shows a typical pattern to conduct custom model validation. The model class overrides the
clean()
method to provide custom model validation and the save()
method
to call full_clean
method. The example code is shown below:
class BaseModel(models.Model):
# model fields
def clean(self, *args, **kwargs):
# add custom validation here
super(BaseModel, self).clean(*args, **kwargs)
def save(self, *args, **kwargs):
self.full_clean()
super(BaseModel, self).save(*args, **kwargs)
Another stack overflow answer shows how to use custom model validation or simply use model field’s built-in validator.
Model field’s validation will not kick in unless the full_clean()
method
is explicitly called. For example, the p2.save()
below would not raise an
exception when called.
class PageModel(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50)
>>> from page.models import PageModel #page app name
>>> p1 = PageModel(name='Page1', slug='page1')
>>> p1.save()
>>> p2 = PageModel(name='Page2', slug='page2#$%')
>>> p2.save() # no error
>>> p2.full_clean() # raise exception
Checking clean_fields()
method source code, it has the following lines.
The f.clean(...)
method calls validation method on a model field.
try:
setattr(self, f.attname, f.clean(raw_value, self))
except ValidationError as e:
errors[f.name] = e.error_list
While model validation is a subsection on a Django documentation page, the
form validation
is on a separate page. Form validation is normally executed when the is_valid()
method is called on a form. A programmer can also trigger form validation
by accessing errors
attribute or call full_clean()
method of a form.
Form validation has a series of steps:
to_python()
method on a field, correct data typevalidation()
method on a fieldrun_validators()
method on a fieldclean()
method on a Field subclass, which calls above three methods and returns the clean dataclean_<fieldname>()
method has access to cleaned_data Python object and returns value that replaces data in cleaned_dataclean()
method of form, for multiple fieldsThe same documetation page has several nice examples, which are based on the model shown below:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
recipients = MultiEmailField()
cc_myself = forms.BooleanField(required=False)
The same page points out that “there are special considerations when overriding
the clean()
method of a ModelForm subclass.”
Chapter 7 of Andrew Pinkham’s Django Unleashed book, titled allowing user
input with forms, has good example on how to override clean_<fieldname>
method. The discussion on model validation and form validation in this chapter
is better than other Django books I have read.
The form validation steps described in the previous section also apply to
ModelForm validation. In addition to that, Model.full_clean()
method is
triggered after the form’s clean()
method is called. So, model validation
methods are not triggered by model save()
method, but model validation methods
are triggered by ModelForm validation.
This stack overflow question
discusses this exact issue. The accepted answer also has code example on
model validation.
Error messages at the form field level take precedence over the error messages defined at the model field level.