1
0
mirror of https://github.com/django/django.git synced 2025-07-07 03:09:22 +00:00

[soc2009/model-validation] Added note on ComplexValidators and multi field validation

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11436 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Honza Král 2009-08-12 16:37:20 +00:00
parent 244296ffbc
commit b28332cd46

View File

@ -293,53 +293,49 @@ 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
``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.
performing validation on more than one field at a time, so a
``ComplexValidator`` is a good start. The complex validators are run in the
form's ``clean()`` after the individual fields have been validated. This is the
main difference against simple validators. It is important to realize that,
even if defined in very similar way, simple and complex validators are run in
different places in the code. Simple validators on a field (single data point),
complex validator on a form (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.
By the time the field's complex validators are called, all the individual field
clean methods will have been run (the previous two sections), so the
validator's ``all_values`` argument 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 way 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::
Complex validator is run on the form, but reports it's error with the field. As
with simple validators, all complex validators for a given field are run if the
field has passed cleaning and their errors are aggregated and reported.
To create a complex validator, simply subclass ``ComplexValidator`` and supply
your validation logic in it's ``__call__()`` method, for example::
class ValidateHelpInSubjectIfCCingMyself(ComplexValidator):
def __call__(self, all_values={}, obj=None):
cc_myself = self.get_value('cc_myself', all_values, obj)
if cc_myself and "help" not in value:
raise forms.ValidationError("Did not send for 'help' in "
"the subject despite CC'ing yourself.")
class ContactForm(forms.Form):
# Everything as before.
subject = forms.CharField(max_length=100,
validators=[ValidateHelpInSubjectIfCCingMyself()])
...
# Everything as before.
def clean(self):
cleaned_data = self.cleaned_data
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 forms.ValidationError("Did not send for 'help' in "
"the subject despite CC'ing yourself.")
# Always return the full collection of cleaned data.
return cleaned_data
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.
The second approach 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::
The second approach might involve assigning the error message to both fields
involved. To do this we will move the validation code to the form's ``clean()``
method, which is a convenient place for form-wide validation and has access to
the form instance and it's ``_errors`` property. Our new code (replacing the
previous sample) looks like this::
from django.forms.util import ErrorList