From 1f98e981060e099498e30e11b08a76ab26bae065 Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Mon, 4 Jan 2010 23:52:03 +0000 Subject: [PATCH] [soc2009/model-validation] Removed references in the docs to ComplexValidator. Refs [12498]. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@12091 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/ref/forms/validation.txt | 113 +++++++++++++++------------------- 1 file changed, 48 insertions(+), 65 deletions(-) diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt index 0f2b545df5..54d755f3ac 100644 --- a/docs/ref/forms/validation.txt +++ b/docs/ref/forms/validation.txt @@ -23,25 +23,10 @@ of them to the form submitter, it is possible to pass a list of errors to the ``ValidationError`` constructor. Most validation can be done using `validators`_ - simple helpers that can be -reused easily. There are two types of validators: - - * Simple validators are simple functions (or callables) that take a single - argument and raises ``ValidationError`` on invalid input. Simple - validators are run inside the ``run_validators`` method that is called - from ``Field.clean`` once the value is validated by the field's methods. - - * Complex validators are instances of ``ComplexValidator`` class and, - unlike simple validators, can access not just one value, but all at once. - These are perfectly suited for cross field validation (one of N fields - must be supplied, this field must equal that etc.) - - -.. warning:: - - Since complex validators must have access to all cleaned values, they must - be run after individual fields have been cleaned. This means that these are run - in ``Form.full_clean`` and not inside ``Field.clean`` with simple validators. - +reused easily. Validators are simple functions (or callables) that take a single +argument and raise ``ValidationError`` on invalid input. Validators are run +inside the ``run_validators`` method that is called from ``Field.clean`` once +the value is validated by the field's methods. Validation of a Form is split into several steps, which can be customized or overridden: @@ -59,10 +44,10 @@ overridden: raises ``ValidationError`` on any error. This method does not return anything and shouldn't alter the value. - * Simple validators are run in the ``run_validators`` method. This method + * Validators are run in the ``run_validators`` method. This method aggregates all the errors from all validators run into a single ``ValidationError``. - + * The ``clean()`` method on a Field subclass. This is responsible for running ``to_python``, ``validate`` and ``run_validators`` in the correct order and propagate their errors. If, at any time, any of the methods @@ -91,11 +76,6 @@ overridden: should return the cleaned data, regardless of whether it changed anything or not. - * The Field's complex validators are run after all the fields have been - cleaned and only on those fields that passed validation. Errors from - individual validators are aggregated and all the errors are raised for a - given field. - * The Form subclass's ``clean()`` method. This method can perform any validation that requires access to multiple fields from the form at once. This is where you might put in things to check that if field ``A`` @@ -115,9 +95,8 @@ overridden: 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_()``. Once those two methods are run for every -field, the complex validators are run for every field and, finally, -``Form.clean()`` method, or its override, is executed. +``clean_()``. Finally, once those two methods are run for every +field, the ``Form.clean()`` method, or its override, is executed. Examples of each of these methods are provided below. @@ -293,49 +272,53 @@ 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 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). +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. -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. +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. -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.") - +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:: class ContactForm(forms.Form): - 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") -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:: + 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:: from django.forms.util import ErrorList