123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- =========================
- Form and field validation
- =========================
- .. currentmodule:: django.forms
- Form validation happens when the data is cleaned. If you want to customize
- this process, there are various places to make changes, each one serving a
- different purpose. Three types of cleaning methods are run during form
- processing. These are normally executed when you call the ``is_valid()``
- method on a form. There are other things that can also trigger cleaning and
- validation (accessing the ``errors`` attribute or calling ``full_clean()``
- directly), but normally they won't be needed.
- In general, any cleaning method can raise ``ValidationError`` if there is a
- problem with the data it is processing, passing the relevant information to
- the ``ValidationError`` constructor. :ref:`See below <raising-validation-error>`
- for the best practice in raising ``ValidationError``. If no ``ValidationError``
- is raised, the method should return the cleaned (normalized) data as a Python
- object.
- Most validation can be done using `validators`_ - helpers that can be reused.
- Validators are functions (or callables) that take a single argument and raise
- ``ValidationError`` on invalid input. Validators are run after the field's
- ``to_python`` and ``validate`` methods have been called.
- Validation of a form is split into several steps, which can be customized or
- overridden:
- * The ``to_python()`` method on a ``Field`` is the first step in every
- validation. It coerces the value to a correct datatype and raises
- ``ValidationError`` if that is not possible. This method accepts the raw
- value from the widget and returns the converted value. For example, a
- ``FloatField`` will turn the data into a Python ``float`` or raise a
- ``ValidationError``.
- * The ``validate()`` method on a ``Field`` handles field-specific validation
- that is not suitable for a validator. It takes a value that has been
- coerced to a correct datatype and raises ``ValidationError`` on any error.
- This method does not return anything and shouldn't alter the value. You
- should override it to handle validation logic that you can't or don't
- want to put in a validator.
- * The ``run_validators()`` method on a ``Field`` runs all of the field's
- validators and aggregates all the errors into a single
- ``ValidationError``. You shouldn't need to override this method.
- * The ``clean()`` method on a ``Field`` subclass is responsible for running
- ``to_python()``, ``validate()``, and ``run_validators()`` in the correct
- order and propagating their errors. If, at any time, any of the methods
- raise ``ValidationError``, the validation stops and that error is raised.
- This method returns the clean data, which is then inserted into the
- ``cleaned_data`` dictionary of the form.
- * The ``clean_<fieldname>()`` method is called on a form subclass -- where
- ``<fieldname>`` is replaced with the name of the form field attribute.
- This method does any cleaning that is specific to that particular
- attribute, unrelated to the type of field that it is. This method is not
- passed any parameters. You will need to look up the value of the field
- in ``self.cleaned_data`` and remember that it will be a Python object
- at this point, not the original string submitted in the form (it will be
- in ``cleaned_data`` because the general field ``clean()`` method, above,
- has already cleaned the data once).
- For example, if you wanted to validate that the contents of a
- ``CharField`` called ``serialnumber`` was unique,
- ``clean_serialnumber()`` would be the right place to do this. You don't
- need a specific field (it's a ``CharField``), but you want a
- formfield-specific piece of validation and, possibly, cleaning/normalizing
- the data.
- The return value of this method replaces the existing value in
- ``cleaned_data``, so it must be the field's value from ``cleaned_data`` (even
- if this method didn't change it) or a new cleaned value.
- * The form subclass's ``clean()`` method can perform validation that requires
- access to multiple form fields. This is where you might put in checks such as
- "if field ``A`` is supplied, field ``B`` must contain a valid email address".
- This method can return a completely different dictionary if it wishes, which
- will be used as the ``cleaned_data``.
- Since the field validation methods have been run by the time ``clean()`` is
- called, you also have access to the form's ``errors`` attribute which
- contains all the errors raised by cleaning of individual fields.
- Note that any errors raised by your :meth:`Form.clean()` override will not
- be associated with any field in particular. They go into a special
- "field" (called ``__all__``), which you can access via the
- :meth:`~django.forms.Form.non_field_errors` method if you need to. If you
- want to attach errors to a specific field in the form, you need to call
- :meth:`~django.forms.Form.add_error()`.
- Also note that there are special considerations when overriding
- the ``clean()`` method of a ``ModelForm`` subclass. (see the
- :ref:`ModelForm documentation
- <overriding-modelform-clean-method>` for more information)
- These methods are run in the order given above, one field at a time. That is,
- for each field in the form (in the order they are declared in the form
- definition), the ``Field.clean()`` method (or its override) is run, then
- ``clean_<fieldname>()``. Finally, once those two methods are run for every
- field, the :meth:`Form.clean()` method, or its override, is executed whether
- or not the previous methods have raised errors.
- Examples of each of these methods are provided below.
- As mentioned, any of these methods can raise a ``ValidationError``. For any
- field, if the ``Field.clean()`` method raises a ``ValidationError``, any
- field-specific cleaning method is not called. However, the cleaning methods
- for all remaining fields are still executed.
- .. _raising-validation-error:
- Raising ``ValidationError``
- ===========================
- In order to make error messages flexible and easy to override, consider the
- following guidelines:
- * Provide a descriptive error ``code`` to the constructor::
- # Good
- ValidationError(_("Invalid value"), code="invalid")
- # Bad
- ValidationError(_("Invalid value"))
- * Don't coerce variables into the message; use placeholders and the ``params``
- argument of the constructor::
- # Good
- ValidationError(
- _("Invalid value: %(value)s"),
- params={"value": "42"},
- )
- # Bad
- ValidationError(_("Invalid value: %s") % value)
- * Use mapping keys instead of positional formatting. This enables putting
- the variables in any order or omitting them altogether when rewriting the
- message::
- # Good
- ValidationError(
- _("Invalid value: %(value)s"),
- params={"value": "42"},
- )
- # Bad
- ValidationError(
- _("Invalid value: %s"),
- params=("42",),
- )
- * Wrap the message with ``gettext`` to enable translation::
- # Good
- ValidationError(_("Invalid value"))
- # Bad
- ValidationError("Invalid value")
- Putting it all together::
- raise ValidationError(
- _("Invalid value: %(value)s"),
- code="invalid",
- params={"value": "42"},
- )
- Following these guidelines is particularly necessary if you write reusable
- forms, form fields, and model fields.
- While not recommended, if you are at the end of the validation chain
- (i.e. your form ``clean()`` method) and you know you will *never* need
- to override your error message you can still opt for the less verbose::
- ValidationError(_("Invalid value: %s") % value)
- The :meth:`Form.errors.as_data() <django.forms.Form.errors.as_data()>` and
- :meth:`Form.errors.as_json() <django.forms.Form.errors.as_json()>` methods
- greatly benefit from fully featured ``ValidationError``\s (with a ``code`` name
- and a ``params`` dictionary).
- Raising multiple errors
- -----------------------
- If you detect multiple errors during a cleaning method and wish to signal all
- of them to the form submitter, it is possible to pass a list of errors to the
- ``ValidationError`` constructor.
- As above, it is recommended to pass a list of ``ValidationError`` instances
- with ``code``\s and ``params`` but a list of strings will also work::
- # Good
- raise ValidationError(
- [
- ValidationError(_("Error 1"), code="error1"),
- ValidationError(_("Error 2"), code="error2"),
- ]
- )
- # Bad
- raise ValidationError(
- [
- _("Error 1"),
- _("Error 2"),
- ]
- )
- Using validation in practice
- ============================
- The previous sections explained how validation works in general for forms.
- Since it can sometimes be easier to put things into place by seeing each
- feature in use, here are a series of small examples that use each of the
- previous features.
- .. _validators:
- Using validators
- ----------------
- Django's form (and model) fields support use of utility functions and classes
- known as validators. A validator is a callable object or function that takes a
- value and returns nothing if the value is valid or raises a
- :exc:`~django.core.exceptions.ValidationError` if not. These can be passed to a
- field's constructor, via the field's ``validators`` argument, or defined on the
- :class:`~django.forms.Field` class itself with the ``default_validators``
- attribute.
- Validators can be used to validate values inside the field, let's have a look
- at Django's ``SlugField``::
- from django.core import validators
- from django.forms import CharField
- class SlugField(CharField):
- default_validators = [validators.validate_slug]
- As you can see, ``SlugField`` is a ``CharField`` with a customized validator
- that validates that submitted text obeys to some character rules. This can also
- be done on field definition so::
- slug = forms.SlugField()
- is equivalent to::
- slug = forms.CharField(validators=[validators.validate_slug])
- Common cases such as validating against an email or a regular expression can be
- handled using existing validator classes available in Django. For example,
- ``validators.validate_slug`` is an instance of
- a :class:`~django.core.validators.RegexValidator` constructed with the first
- argument being the pattern: ``^[-a-zA-Z0-9_]+\Z``. See the section on
- :doc:`writing validators </ref/validators>` to see a list of what is already
- available and for an example of how to write a validator.
- Form field default cleaning
- ---------------------------
- Let's first create a custom form field that validates its input is a string
- containing comma-separated email addresses. The full class looks like this::
- from django import forms
- from django.core.validators import validate_email
- class MultiEmailField(forms.Field):
- def to_python(self, value):
- """Normalize data to a list of strings."""
- # Return an empty list if no input was given.
- if not value:
- return []
- return value.split(",")
- def validate(self, value):
- """Check if value consists only of valid emails."""
- # Use the parent's handling of required fields, etc.
- super().validate(value)
- for email in value:
- validate_email(email)
- Every form that uses this field will have these methods run before anything
- else can be done with the field's data. This is cleaning that is specific to
- this type of field, regardless of how it is subsequently used.
- Let's create a ``ContactForm`` to demonstrate how you'd use this field::
- class ContactForm(forms.Form):
- subject = forms.CharField(max_length=100)
- message = forms.CharField()
- sender = forms.EmailField()
- recipients = MultiEmailField()
- cc_myself = forms.BooleanField(required=False)
- Use ``MultiEmailField`` like any other form field. When the ``is_valid()``
- method is called on the form, the ``MultiEmailField.clean()`` method will be
- run as part of the cleaning process and it will, in turn, call the custom
- ``to_python()`` and ``validate()`` methods.
- Cleaning a specific field attribute
- -----------------------------------
- Continuing on from the previous example, suppose that in our ``ContactForm``,
- we want to make sure that the ``recipients`` field always contains the address
- ``"fred@example.com"``. This is validation that is specific to our form, so we
- don't want to put it into the general ``MultiEmailField`` class. Instead, we
- write a cleaning method that operates on the ``recipients`` field, like so::
- from django import forms
- from django.core.exceptions import ValidationError
- class ContactForm(forms.Form):
- # Everything as before.
- ...
- def clean_recipients(self):
- data = self.cleaned_data["recipients"]
- if "fred@example.com" not in data:
- raise ValidationError("You have forgotten about Fred!")
- # Always return a value to use as the new cleaned data, even if
- # this method didn't change it.
- return data
- .. _validating-fields-with-clean:
- Cleaning and validating fields that depend on each other
- --------------------------------------------------------
- Suppose we add another requirement to our contact form: if the ``cc_myself``
- field is ``True``, the ``subject`` must contain the word ``"help"``. We are
- performing validation on more than one field at a time, so the form's
- :meth:`~Form.clean()` method is a good spot to do this. Notice that we are
- talking about the ``clean()`` method on the form here, whereas earlier we were
- writing a ``clean()`` method on a field. It's important to keep the field and
- form difference clear when working out where to validate things. Fields are
- single data points, forms are a collection of fields.
- By the time the form's ``clean()`` method is called, all the individual field
- clean methods will have been run (the previous two sections), so
- ``self.cleaned_data`` will be populated with any data that has survived so
- far. So you also need to remember to allow for the fact that the fields you
- are wanting to validate might not have survived the initial individual field
- checks.
- There are two ways to report any errors from this step. Probably the most
- common method is to display the error at the top of the form. To create such
- an error, you can raise a ``ValidationError`` from the ``clean()`` method. For
- example::
- from django import forms
- from django.core.exceptions import ValidationError
- class ContactForm(forms.Form):
- # Everything as before.
- ...
- def clean(self):
- cleaned_data = super().clean()
- cc_myself = cleaned_data.get("cc_myself")
- subject = cleaned_data.get("subject")
- if cc_myself and subject:
- # Only do something if both fields are valid so far.
- if "help" not in subject:
- raise ValidationError(
- "Did not send for 'help' in the subject despite CC'ing yourself."
- )
- In this code, if the validation error is raised, the form will display an
- error message at the top of the form (normally) describing the problem. Such
- errors are non-field errors, which are displayed in the template with
- ``{{ form.non_field_errors }}``.
- The call to ``super().clean()`` in the example code ensures that any validation
- logic in parent classes is maintained. If your form inherits another that
- doesn't return a ``cleaned_data`` dictionary in its ``clean()`` method (doing
- so is optional), then don't assign ``cleaned_data`` to the result of the
- ``super()`` call and use ``self.cleaned_data`` instead::
- def clean(self):
- super().clean()
- cc_myself = self.cleaned_data.get("cc_myself")
- ...
- The second approach for reporting validation errors might involve assigning the
- error message to one of the fields. In this case, let's assign an error message
- to both the "subject" and "cc_myself" rows in the form display. Be careful when
- doing this in practice, since it can lead to confusing form output. We're
- showing what is possible here and leaving it up to you and your designers to
- work out what works effectively in your particular situation. Our new code
- (replacing the previous sample) looks like this::
- from django import forms
- class ContactForm(forms.Form):
- # Everything as before.
- ...
- def clean(self):
- cleaned_data = super().clean()
- cc_myself = cleaned_data.get("cc_myself")
- subject = cleaned_data.get("subject")
- if cc_myself and subject and "help" not in subject:
- msg = "Must put 'help' in subject when cc'ing yourself."
- self.add_error("cc_myself", msg)
- self.add_error("subject", msg)
- The second argument of ``add_error()`` can be a string, or preferably an
- instance of ``ValidationError``. See :ref:`raising-validation-error` for more
- details. Note that ``add_error()`` automatically removes the field from
- ``cleaned_data``.
|