| 81 | | A simple example |
|---|
| 82 | | ~~~~~~~~~~~~~~~~ |
|---|
| 83 | | |
|---|
| 84 | | Here's a simple example of a custom field that validates its input is a string |
|---|
| | 90 | .. _described later: |
|---|
| | 91 | |
|---|
| | 92 | Form subclasses and modifying field errors |
|---|
| | 93 | ========================================== |
|---|
| | 94 | |
|---|
| | 95 | Sometimes, in a form's ``clean()`` method, you will want to add an error |
|---|
| | 96 | message to a particular field in the form. This won't always be appropriate |
|---|
| | 97 | and the more typical situation is to raise a ``ValidationError`` from |
|---|
| | 98 | ``Form.clean()``, which is turned into a form-wide error that is available |
|---|
| | 99 | through the ``Form.non_field_errors()`` method. |
|---|
| | 100 | |
|---|
| | 101 | When you really do need to attach the error to a particular field, you should |
|---|
| | 102 | store (or amend) a key in the `Form._errors` attribute. This attribute is an |
|---|
| | 103 | instance of a ``django.form.utils.ErrorDict`` class. Essentially, though, it's |
|---|
| | 104 | just a dictionary. There is a key in the dictionary for each field in the form |
|---|
| | 105 | that has an error. Each value in the dictionary is a |
|---|
| | 106 | ``django.form.utils.ErrorList`` instance, which is a list that knows how to |
|---|
| | 107 | display itself in different ways. So you can treat `_errors` as a dictionary |
|---|
| | 108 | mapping field names to lists. |
|---|
| | 109 | |
|---|
| | 110 | If you want to add a new error to a particular field, you should check whether |
|---|
| | 111 | the key already exists in `self._errors` or not. If not, create a new entry |
|---|
| | 112 | for the given key, holding an empty ``ErrorList`` instance. In either case, |
|---|
| | 113 | you can then append your error message to the list for the field name in |
|---|
| | 114 | question and it will be displayed when the form is displayed. |
|---|
| | 115 | |
|---|
| | 116 | There is an example of modifying `self._errors` in the following section. |
|---|
| | 117 | |
|---|
| | 118 | .. admonition:: What's in a name? |
|---|
| | 119 | |
|---|
| | 120 | You may be wondering why is this attribute called ``_errors`` and not |
|---|
| | 121 | ``errors``. Normal Python practice is to prefix a name with an underscore |
|---|
| | 122 | if it's not for external usage. In this case, you are subclassing the |
|---|
| | 123 | ``Form`` class, so you are essentially writing new internals. In effect, |
|---|
| | 124 | you are given permission to access some of the internals of ``Form``. |
|---|
| | 125 | |
|---|
| | 126 | Of course, any code outside your form should never access ``_errors`` |
|---|
| | 127 | directly. The data is available to external code through the ``errors`` |
|---|
| | 128 | property, which populates ``_errors`` before returning it). |
|---|
| | 129 | |
|---|
| | 130 | Another reason is purely historical: the attribute has been called |
|---|
| | 131 | ``_errors`` since the early days of the forms module and changing it now |
|---|
| | 132 | (particularly since ``errors`` is used for the read-only property name) |
|---|
| | 133 | would be inconvenient for a number of reasons. You can use whichever |
|---|
| | 134 | explanation makes you feel more comfortable. The result is the same. |
|---|
| | 135 | |
|---|
| | 136 | Using validation in practice |
|---|
| | 137 | ============================= |
|---|
| | 138 | |
|---|
| | 139 | The previous sections explained how validation works in general for forms. |
|---|
| | 140 | Since it can sometimes be easier to put things into place by seeing each |
|---|
| | 141 | feature in use, here are a series of small examples that use each of the |
|---|
| | 142 | previous features. |
|---|
| | 143 | |
|---|
| | 144 | Form field default cleaning |
|---|
| | 145 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 146 | |
|---|
| | 147 | Let's firstly create a custom form field that validates its input is a string |
|---|
| | 183 | |
|---|
| | 184 | Simply use ``MultiEmailField`` like any other form field. When the |
|---|
| | 185 | ``is_valid()`` method is called on the form, the ``MultiEmailField.clean()`` |
|---|
| | 186 | method will be run as part of the cleaning process. |
|---|
| | 187 | |
|---|
| | 188 | Cleaning a specific field attribute |
|---|
| | 189 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 190 | |
|---|
| | 191 | Continuing on from the previous example, suppose that in our ``ContactForm``, |
|---|
| | 192 | we want to make sure that the ``recipients`` field always contains the address |
|---|
| | 193 | ``"fred@example.com"``. This is validation that is specific to our form, so we |
|---|
| | 194 | don't want to put it into the general ``MultiEmailField`` class. Instead, we |
|---|
| | 195 | write a cleaning method that operates on the ``recipients`` field, like so:: |
|---|
| | 196 | |
|---|
| | 197 | class ContactForm(forms.Form): |
|---|
| | 198 | # Everything as before. |
|---|
| | 199 | ... |
|---|
| | 200 | |
|---|
| | 201 | def clean_recipients(self): |
|---|
| | 202 | data = self.cleaned_data['recipients'] |
|---|
| | 203 | if "fred@example.com" not in data: |
|---|
| | 204 | raise forms.ValidationError("You have forgotten about Fred!") |
|---|
| | 205 | |
|---|
| | 206 | # Always return the cleaned data, whether you have changed it or |
|---|
| | 207 | # not. |
|---|
| | 208 | return data |
|---|
| | 209 | |
|---|
| | 210 | Cleaning and validating fields that depend on each other |
|---|
| | 211 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| | 212 | |
|---|
| | 213 | Suppose we add another requirement to our contact form: if the ``cc_myself`` |
|---|
| | 214 | field is ``True``, the ``subject`` must contain the word ``"help"``. We are |
|---|
| | 215 | performing validation on more than one field at a time, so the form's |
|---|
| | 216 | ``clean()`` method is a good spot to do this. Notice that we are talking about |
|---|
| | 217 | the ``clean()`` method on the form here, whereas earlier we were writing a |
|---|
| | 218 | ``clean()`` method on a field. It's important to keep the field and form |
|---|
| | 219 | difference clear when working out where to validate things. Fields are single |
|---|
| | 220 | data points, forms are a collection of fields. |
|---|
| | 221 | |
|---|
| | 222 | By the time the form's ``clean()`` method is called, all the individual field |
|---|
| | 223 | clean methods will have been run (the previous two sections), so |
|---|
| | 224 | ``self.cleaned_data`` will be populated with any data that has survived so |
|---|
| | 225 | far. So you also need to remember to allow for the fact that the fields you |
|---|
| | 226 | are wanting to validate might not have survived the initial individual field |
|---|
| | 227 | checks. |
|---|
| | 228 | |
|---|
| | 229 | There are two way to report any errors from this step. Probably the most |
|---|
| | 230 | common method is to display the error at the top of the form. To create such |
|---|
| | 231 | an error, you can raise a ``ValidationError`` from the ``clean()`` method. For |
|---|
| | 232 | example:: |
|---|
| | 233 | |
|---|
| | 234 | class ContactForm(forms.Form): |
|---|
| | 235 | # Everything as before. |
|---|
| | 236 | ... |
|---|
| | 237 | |
|---|
| | 238 | def clean(self): |
|---|
| | 239 | cleaned_data = self.cleaned_data |
|---|
| | 240 | cc_myself = cleaned_data.get("cc_myself") |
|---|
| | 241 | subject = cleaned_data.get("subject") |
|---|
| | 242 | |
|---|
| | 243 | if cc_myself and subject: |
|---|
| | 244 | # Only do something if both fields are valid so far. |
|---|
| | 245 | if "help" not in subject: |
|---|
| | 246 | raise forms.ValidationError("Did not send for 'help' in " |
|---|
| | 247 | "the subject despite CC'ing yourself.") |
|---|
| | 248 | |
|---|
| | 249 | # Always return the full collection of cleaned data. |
|---|
| | 250 | return cleaned_data |
|---|
| | 251 | |
|---|
| | 252 | In this code, if the validation error is raised, the form will display an |
|---|
| | 253 | error message at the top of the form (normally) describing the problem. |
|---|
| | 254 | |
|---|
| | 255 | The second approach might involve assigning the error message to one of the |
|---|
| | 256 | fields. In this case, let's assign an error message to both the "subject" and |
|---|
| | 257 | "cc_myself" rows in the form display. Be careful when doing this in practice, |
|---|
| | 258 | since it can lead to confusing form output. We're showing what is possible |
|---|
| | 259 | here and leaving it up to you and your designers to work out what works |
|---|
| | 260 | effectively in your particular situation. Our new code (replacing the previous |
|---|
| | 261 | sample) looks like this:: |
|---|
| | 262 | |
|---|
| | 263 | from django.forms.utils import ErrorList |
|---|
| | 264 | |
|---|
| | 265 | class ContactForm(forms.Form): |
|---|
| | 266 | # Everything as before. |
|---|
| | 267 | ... |
|---|
| | 268 | |
|---|
| | 269 | def clean(self): |
|---|
| | 270 | cleaned_data = self.cleaned_data |
|---|
| | 271 | cc_myself = cleaned_data.get("cc_myself") |
|---|
| | 272 | subject = cleaned_data.get("subject") |
|---|
| | 273 | |
|---|
| | 274 | if cc_myself and subject and "help" not in subject: |
|---|
| | 275 | # We know these are not in self._errors now (see discussion |
|---|
| | 276 | # below). |
|---|
| | 277 | msg = u"Must put 'help' in subject when cc'ing yourself." |
|---|
| | 278 | self._errors["cc_myself"] = ErrorList([msg]) |
|---|
| | 279 | self._errors["subject"] = ErrorList([msg]) |
|---|
| | 280 | |
|---|
| | 281 | # These fields are no longer valid. Remove them from the |
|---|
| | 282 | # cleaned data. |
|---|
| | 283 | del cleaned_data["cc_myself"] |
|---|
| | 284 | del cleaned_data["subject"] |
|---|
| | 285 | |
|---|
| | 286 | # Always return the full collection of cleaned data. |
|---|
| | 287 | return cleaned_data |
|---|
| | 288 | |
|---|
| | 289 | As you can see, this approach requires a bit more effort, not withstanding the |
|---|
| | 290 | extra design effort to create a sensible form display. The details are worth |
|---|
| | 291 | noting, however. Firstly, earlier we mentioned that you might need to check if |
|---|
| | 292 | the field name keys already exist in the ``_errors`` dictionary. In this case, |
|---|
| | 293 | since we know the fields exist in ``self.cleaned_data``, they must have been |
|---|
| | 294 | valid when cleaned as individual fields, so there will be no corresonding |
|---|
| | 295 | entries in ``_errors``. |
|---|
| | 296 | |
|---|
| | 297 | Secondly, once we have decided that the combined data in the two fields we are |
|---|
| | 298 | considering aren't valid, we must remember to remove them from the |
|---|
| | 299 | ``cleaned_data``. |
|---|
| | 300 | |
|---|
| | 301 | In fact, Django will currently completely wipe out the ``cleaned_data`` |
|---|
| | 302 | dictionary if there are any errors in the form. However, this behaviour may |
|---|
| | 303 | change in the future, so it's not a bad idea to clean up after yourself in the |
|---|
| | 304 | first place. |
|---|
| | 305 | |
|---|