[Django]-Django forms: how do is_valid() and validation errors work together?

3👍

A Form instance is either bound to a set of data, or unbound.

  • If it’s bound to a set of data, it’s capable of validating that data
  • If it’s unbound, it cannot do validation
class Form:
    ...
    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        return self.is_bound and not self.errors

With a bound Form instance, call the .is_valid() method to run validation and return a boolean designating whether the form is bound and the data is valid(has no errors)

  • self.is_bound check whether the data is passed to form
  • self.errors check the property error, which will trigger the full validation process(if _errors not populated yet)
class Form:
    ...
    @property
    def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        if self._errors is None:
            self.full_clean()
        return self._errors

After that, self.full_clean will trigger a series of validation, including cleaning individual field(self._clean_fields), whole form(self._clean_form), and post clean hooker(_self._post_clean).

In the process, any client side code that raises ValidationError (i.e raise ValidationError("Email already exists")) will be properly handled by Form itself by adding these errors to the form._errors attribute, which eventually, you could access through form.errors property

class BaseForm:
    ...
    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and self.cleaned_data.
        """
        self._errors = ErrorDict()
        self.cleaned_data = {}
        
        # these three methods will populate the _errors, cleaned_data attribute
        self._clean_fields()
        self._clean_form()
        self._post_clean()

    def _clean_fields(self):
        # simplified for demonstration purpose
        for name, field in self.fields.items():
            try:
                validate(field)
            #  your ValidationError("Email already exists") will be catched here
            except ValidationError as e:
                # add errors to `form._error` dict
                self.add_error(name, e)

Leave a comment