Django form processing: Initialization, field access, validation and error handling.
Because Django form processing can have a great deal of variations, I'll start the discussion from the same form and view method explained in the previous section, now consolidated in listing 6-8.
Listing 6-8 Django form class with backing processing view method
from django import forms from django.shortcuts import render class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea) def contact(request): if request.method == 'POST': # POST, generate form with data from the request form = ContactForm(request.POST) # Reference is now a bound instance with user data sent in POST # process data, insert into DB, generate email, redirect to a new URL,etc else: # GET, generate blank form form = ContactForm() # Reference is now an unbound (empty) form # Reference form instance (bound/unbound) is sent to template for rendering return render(request,'about/contact.html',{'form':form})
Initialize forms : initial for fields & forms, __init__ method, label_suffix, auto_id, field_order and use_required_attribute
When users request a page backed
by a Django form they're sent an empty form (a.k.a.
unbound form) -- represented by the GET section and
ContactForm()
instance in listing 6-8. Although the
form is always empty from a user data perspective, the form can
contain data from as part of its initialization sequence (e.g. a
pre-filled form with a user's email or name).
To perform this kind of Django
form initialization you have three options. The first technique
consists of initializing the form with a dictionary of values via
the initial
argument directly on the form instance
declared in the view method. Listing 6-9 shows this technique.
Listing 6-9 Django form instance with initial argument declared in view method
def contact(request): .... .... else: # GET, generate blank form form = ContactForm(initial={'email':'johndoe@coffeehouse.com','name':'John Doe'}) # Form is now initialized for first presentation to display these values # Reference form instance (bound/unbound) is sent to template for rendering return render(request,'about/contact.html',{'form':form})
The ContactForm()
now uses
initial={'email':'johndoe@coffeehouse.com','name':'John
Doe'}
to generate a pre-filled form instance with values for
the email and name fields so end users don't have to go to the
trouble of typing these values from scratch themselves.
The technique in listing 6-9 is
intended for one-time or instance specific initialization values.
If you want to constantly provide the same value for a given form
field, a more suitable technique is to use the same
initial
argument directly on a form field, as show in
listing 6-10.
Listing 6-10 Django form fields with initial argument
from django import forms class ContactForm(forms.Form): name = forms.CharField(required=False,initial='Please provide your name') email = forms.EmailField(label='Your email', initial='We need your email') comment = forms.CharField(widget=forms.Textarea) def contact(request): .... .... else: # GET, generate blank form form = ContactForm() # Form is now initialized for first presentation and is filled with initial values in form definition # Reference form instance (bound/unbound) is sent to template for rendering return render(request,'about/contact.html',{'form':form})
Notice in listing 6-10 how it's
now the form fields in the form class equipped with the
initial
argument. It's worth mentioning that if you
use the initial
argument on both the form instance and
form fields, form instance values take precedence over any form
field (e.g. if you combine the statements from listing 6-9 and listing 6-10, the form will always be pre-filled with
email='johndoe@coffeehouse.com' and name='John Doe'
from listing 6-9).
One of the drawbacks of using the
initial
argument in form fields like listing 6-10 is
that values can't change dynamically at run-time (i.e. you can't
personalize the initial
values based on who calls the
form like listing 6-9). To solve this and providing the utmost
flexibility for the most complex form initialization scenarios is a
third technique using the __init__
method of a form
class illustrated in listing 6-11.
Listing 6-11. Django form initialized with __init__ method
from django import forms class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea) def __init__(self, *args, **kwargs): # Get 'initial' argument if any initial_arguments = kwargs.get('initial', None) updated_initial = {} if initial_arguments: # We have initial arguments, fetch 'user' placeholder variable if any user = initial_arguments.get('user',None) # Now update the form's initial values if user if user: updated_initial['name'] = getattr(user, 'first_name', None) updated_initial['email'] = getattr(user, 'email', None) # You can also initialize form fields with hardcoded values # or perform complex DB logic here to then perform initialization updated_initial['comment'] = 'Please provide a comment' # Finally update the kwargs initial reference kwargs.update(initial=updated_initial) super(ContactForm, self).__init__(*args, **kwargs) def contact(request): .... .... else: # GET, generate blank form form = ContactForm(initial={'user':request.user,'otherstuff':'otherstuff'}) # Form is now initialized via the form's __init__ method # Reference form instance (bound/unbound) is sent to template for rendering return render(request,'about/contact.html',{'form':form})
The first important aspect in
listing 6-11 is how the form is initialized in the view method,
notice it uses the same initial
argument but the
dictionary values are now
{'user':request.user,'otherstuff':'otherstuff'}
. Do
these values look strange ? The form doesn't even have fields named
user
or otherstuff
, so what's happening
?
These last values are perfectly
valid and in other circumstances would be ignored because the
Django form does indeed have no fields by these names, but since
we'll be manipulating the guts of form initialization process in
the __init__
method, we can access these placeholder
values for indirect initialization purposes. More importantly,
using these placeholder values illustrates how it's possible to use
context data or unrelated form data to initialize Django form
fields.
Next, let's turn our attention to
the Django form's __init__
method, which is invoked
when you create a form instance. The __init__
method's
arguments *args
, **kwargs
are standard
Python syntax -- if you've never seen this last syntax check out
the appendix on Python basics: Methods: Default, optional, *args
& **kwargs arguments.
The first step in
__init__
checks for an initial value and creates a
reference to hold the new values to initialize the form. If an
initial
value is present, a check is made for a
user
value to use these values for the form's actual
name
and email
fields. Next, irrespective
of any passed in values, a direct assignment is made on the form's
comment
field.
Finally, the form's
initial
reference is updated with a new set of values
that reflect the form's actual fields, leading to the form's
initialization using data outside the context of a form (e.g.
request data, database query,etc). As a last step in the
__init__
method, a call is made to the
super()
method so that the base/parent class
initialization process takes place.
It's important to note all the previous initialization techniques keep a form unbound which is a term used to describe form instances that haven't been populated with user data. The term bound is reserved for when a form's field values are populated with user data. This subtle difference between bound and unbound is really important when you enter the validation phase of Django forms described in the next section.
This also means the syntax ContactForm(initial={'email':'johndoe@coffeehouse.com','name':'John Doe'}) is not equivalent to ContactForm({'email':'johndoe@coffeehouse.com','name':'John Doe'}). The first variation uses the initial argument to create an unbound form instance, while the second variation creates a bound form instance by passing the values directly without any argument.
In addition to initializing the
first set of data loaded on a Django form, there are four other
initialization options for Django forms that influence a form's
layout in templates: label_suffix
,
auto_id
, field_order
and
use_required_attribute
.
When you generate the output for
a Django form in a template -- a topic that's described in detail
later in this chapter in 'Set up the layout for Django forms in
templates' -- form fields are generally accompanied by what's
called a field label, which is another name for a human-friendly
descriptor. For example, if you have fields called
name
and email
, their default labels are
Your name
and Your email
, respectively.
To separate field labels from a field's HTML markup (e.g.
<input type="text">
) Django defines a label
suffix, which defaults to :
(the colon symbol) to
produce output with the pattern <field_label>:<input
type="<field_type>">
. Through the
label_suffix
you can define a custom label suffix
symbol to separate every form field from its label. So for example,
the ContactForm(label_suffix='...')
syntax outputs
every form field label separated by ...
(e.g.
Your email...<input type="text">
).
Tip Individual form fields can also use the label_suffix attribute, see the Django form field types section. If label_suffix is declared in both a form field and form initialization, the former takes precedence.
Another initialization option for
Django forms is auto_id
which automatically generates
an id
and label
for every form field. By
default, a Django form is always set to auto_id=True
so you'll always get auto generated HTML ids and labels when
outputting a form with form.as_table()
, as illustrated
in the first part of listing 6-12:
Listing 6-12 Django form with automatic ids (default auto_id=True option) and no automatic ids auto_id=False option
<!-- Option 1, default auto_id=True --> <tr> <th><label for="id_name">Name:</label></th> <td><input id="id_name" name="name" type="text" /></td> </tr> <tr> <th><label for="id_email">Your email:</label></th> <td><input id="id_email" name="email" type="email" /></td> </tr> <tr> <th><label for="id_comment">Comment:</label></th> <td><textarea cols="40" id="id_comment" name="comment" rows="10"></textarea></td> </tr> <!-- Option 2 auto_id=False --> <tr> <th>Name:</th> <td><input name="name" type="text" /></td> </tr> <tr> <th>Your email:</th> <td><input name="email" type="email" /></td> </tr> <tr> <th>Comment:</th> <td><textarea cols="40" name="comment" rows="10">\r\n</textarea></td> </tr>
Notice how the field
labels in the top output of listing 6-12 are wrapped around
<label for="id_field_name"> <label>
and
the fields HTML tags includes the id="id_field_name"
attribute. In most cases this is a desirable output as it allows
fields to be easily referenced for purposes of attaching JavaScript
events or CSS classes. However, for other circumstances
auto_id=True
can produce very verbose output and
inclusively conflicting HTML tags (e.g. if you have two form
instances of the same type on the same page, there will be two
identical ids).
To turn off the auto generation
of ids and labels you can initialize a form with the
auto_id=False
option. For example, the
ContactForm(auto_id=False)
syntax generates the output
presented in the second half of listing 6-12.
Another initialization option for
unbound form instances that influences a form's layout is the
field_order
option. By default, form fields are output
in the order they're declared, so the form definition in listing
6-10 follows the output order: name
,
email
, comment
. You can override this
default field output by using the field_order
option
which accepts a list of field names with the desired output order.
The field_order
option can be declared as part of the
initialization process or inclusively as if it were a form
field.
For example, the
ContactForm(field_order=['email','comment','name'])
syntax ensures the email
field is output first,
followed by comment
and name
. It's worth
mentioning the field_order
option can accept an
incomplete field list, such as
ContactForm(field_order=['email'])
which outputs the
email
field followed by the remaining form fields in
their declared order, in this case name
and then
comment
. If you're constantly setting
field_order
to initialize a form's field order, a
quicker solution is to set a default field_order
as
part of the form itself:
class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea) field_order = ['email','comment','name'] # Sets order email,comment,name
If you declare
field_order
as part of the form itself and on
initialization, the initialization field_order
takes
precedence. The upcoming section 'Set up the layout for Django
forms in templates' contains more details on the practical use of field_order
in templates.
Finally, the
use_required_attribute
option allows you to set the
overall use of the HTML 5 required
attribute. By
default use_required_attribute=True
, which means all
required form fields are output with the HTML 5
required
attribute, ensuring browsers enforces these
form fields are always provided. You can disable the use of this
HTML 5 client-side validation required
attribute by
initializing a form with use_required_attribute=False
.
Note that setting use_required_attribute=False
does
not influence the Django server-side validation of a form field
(e.g. if a form field is required, Django server-side validation
still catches fields that aren't provided, irrespective of the
use_required_attribute
option).
Accessing form values: request.POST and cleaned_data.
Once a user fills out a Django form, the form is sent back to the server to be processed and perform an action with the user data (e.g. create an order, send an email, save the data to the database) -- a step which is represented in the POST section in listing 6-8.
One of the key advantages of
Django form processing is you can use the request.POST
variable to create a bound form instance. But although the
request.POST
variable is the initial access point to
populate a Django form with user data, you shouldn't use this data
beyond initializing a form instance, as the data in
request.POST
is too raw for direct access.
For example, in
request.POST
you still don't know if the user provided
data is valid. In addition, the data in request.POST
is still treated as strings, so if your Django form happens to have
an IntegerField()
or DateField()
it still
needs to be converted manually to the expected data type (e.g. '6'
to 6 integer, '2017-01-01' to 2017-01-01 datetime), which is just
unnecessary work that another another part of Django forms deals
with.
Once you have a bound form
generated with the request.POST
variable, you can then
access each of the form's field values through the
cleaned_data
dictionary. For example, if the bound
form has a form field called name
you can use the
syntax form.cleaned_data['name']
to access the user
provided name
value. More importantly, if the form
field is an IntegerField()
named age
the
syntax form.cleaned_data['age']
produces an integer
value, a formatting behavior that also applies to other form fields
with non-string data types (e.g. DateField()
).
Caution You can't access cleaned_data until is_valid() is called on the form.
By design, it isn't possible to
access a form instance's cleaned_data
dictionary
unless you first call the is_valid()
method. If you
try to access cleaned_data
before calling
is_valid()
you'll get the error AttributeError:
'form_reference' object has no attribute 'cleaned_data'
.
If you think about this for a
second it's good practice, after all, why would you want to access
data that hasn't been validated ? The next section describes the
is_valid()
method.
Validating form values : is_valid(), validators, clean_<field>() and clean()
The is_valid()
method is one of the more important parts of Django form
processing. Once you create a bound form with
request.POST
, you call the is_valid()
method on the instance to determine if the included values comply
with a form's field definitions (e.g. if an
EmailField()
value is a valid email). Although the
is_valid()
method returns a boolean True
or False
value, it has two important side-effects:
- Calling
is_valid()
also creates thecleaned_data
dictionary on the form instance to hold the form field values that passed validation rules. - Calling
is_valid()
also creates theerrors
dictionary on the form instance to hold the form errors for each of the fields that didn't pass the validation rules.
Listing 6-13 illustrates a
modified version of listing 6-8 with the is_valid()
method.
Listing 6-13 Django form is_valid() method for form processing
from django.http import HttpResponseRedirect def contact(request): if request.method == 'POST': # POST, generate form with data from the request form = ContactForm(request.POST) # Reference is now a bound instance with user data sent in POST # Call is_valid() to validate data and create cleaned_data and errors dict if form.is_valid(): # Form data is valid, you can now access validated values in the cleaned_data dict # e.g. form.cleaned_data['email'] # process data, insert into DB, generate email # Redirect to a new URL return HttpResponseRedirect('/about/contact/thankyou') else: pass # Not needed # is_valid() method created errors dict, so form reference now contains errors # this form reference drops to the last return statement where errors # can then be presented accessing form.errors in a template else: # GET, generate blank form form = ContactForm() # Reference is now an unbound (empty) form # Reference form instance (bound/unbound) is sent to template for rendering return render(request,'about/contact.html',{'form':form})
Notice in listing 6-13 that right
after a bound form instance is created a call is made to the
is_valid()
method. If all the form field values comply
against the form field data types, we enter a conditional where
it's possible to access the form values through the
cleaned_data
dictionary, perform whatever business
logic is necessary and relinquish control to another page, which in
listing 6-13 is to perform redirect.
If any of the form field values
fails to pass a rule, then is_valid()
returns
False
and in the process creates an
errors
dictionary with details about the values that
failed to pass their rules. Because of this last automatic creation
of errors
, all that's needed after
is_valid()
returns False
is to return the
same form instance in order to display the errors
dictionary to an end user so he can correct his mistakes.
But as important as the
is_valid()
method is to Django form processing, its
validation is just done against a form field's data type. For
example, is_valid()
can validate if a value is left
empty, if a value matches a given number range or even if a value
is a valid date, in essence anything supported by Django form field
types.
But what if you want to perform
more sophisticated validation after is_valid()
? Like
checking a value against a database before deeming it valid or
checking two values against one another (e.g. a provided zip code
value against a provided city value). While you can add these
validation checks directly after the is_valid()
call,
Django offers three more efficient ways to enforce advanced rules
by adding them to the form field or form class definition.
If you want a reusable validation
mechanism you can use across multiple Django form fields, the best
choice is a validator assigned through a form field's
validators
option. The validators
form
field option expects a list of methods designed to raise a
forms.ValidationError
error in case a value doesn't
comply with expected rules. Listing 6-14 illustrates a Django form
with one of its fields using a custom validator method via the
validators
option.
Listing 6-14 Django form field validators option with custom validator method for form processing
from django import forms import re def validate_comment_word_count(value): count = len(value.split()) if count < 30: raise forms.ValidationError(('Please provide at least a 30 word message, %(count)s words is not descriptive enough'), params={'count': count},) class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea,validators=[validate_comment_word_count])
The first section in listing 6-14
shows the custom validate_command_word_count()
method,
which (rudimentarly) checks if message
has at least
thirty words. If the method's input is not at least thirty words,
Django's forms.ValidationError
error is raised to
indicate a rule violation.
In the bottom half of listing
6-14 you can see a modified ContactForm
where the
comment
field uses the
validators=[validate_csv]
option. This tells Django
that after is_valid()
is run and all the form fields
have been checked for errors against their data types, it should
also run the validate_comment_word_count
validator
method against the value provided for the comment
field. If the comment value does not comply with this rule, then a
ValidatioError
error is raised which is added to the
form errors
dictionary -- the same one described in
the past section which is used to check field values against their
data types.
As you can see from the example
in listing 6-14, you can equally reuse the custom
validate_comment_word_count()
method on any other form
field in the same form or in another Django form through the
validators
option. In addition, you can also apply
multiple validators to a field since the validators
option accepts a list of validators. Finally, it's worth mentioning
the django.core.validators
package contains a series
of validators you can also reuse[2] and which are
used behind by the scenes by certain form field data types.
In addition to the form field
validators
option, it's also possible to add
validation form rules through the
clean_<field>()
and clean()
methods
which are created as part of a Django form class -- just like
__init__()
described earlier. Just like methods
specified in the form field validators
option,
clean_<field>()
and clean()
methods
are automatically invoked when the is_valid()
method
is run. Listing 6-15 illustrates the use of two
clean_<field>()
methods.
Listing 6-15 Django form field validation with clean_<field>() methods
from django import forms class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea) def clean_name(self): # Get the field value from cleaned_data dict value = self.cleaned_data['name'] # Check if the value is all upper case if value.isupper(): # Value is all upper case, raise an error raise forms.ValidationError("""Please don't use all upper case for your name, use lower case""",code='uppercase') # Always return value return value def clean_email(self): # Get the field value from cleaned_data dict value = self.cleaned_data['email'] # Check if the value end in @hotmail.com if value.endswith('@hotmail.com'): # Value ends in @hotmail.com, raise an error raise forms.ValidationError("""Please don't use a hotmail email, we simply don't like it""",code='hotmail') # Always return value return value
In listing 6-15 there are two
clean_<field>()
methods to add validation rules
for the name
and email
fields. Django
automatically searches for form methods prefixed with
clean_
and attempts to match a form's field names to
the remaining name, to enforce validation on the field in question.
This means you can have as many clean_<field>()
methods as form fields.
The logic inside each
clean_<field>()
method follows a similar pattern
to validators
methods. First you extract a field's
value from the form's cleaned_data
dictionary via the
self
reference which represents the form instance.
Next, you run whatever rule or logic you want against the field
value, if you deem the value doesn't comply you raise a
forms.ValidationError
which adds the error to the form
instance. Finally, and this is only different to
validators
methods, you must return the field value
irrespective of raising an error or changing its value.
Sometimes it's necessary to apply
a rule that doesn't necessarily belong to a specific field, in
which case the generic clean()
method is the preferred
approach vs. a clean_<field>()
method. Listing
6-16 illustrates the use of the clean()
method.
Listing 6-16. Django form field validation with clean() method
from django import forms class ContactForm(forms.Form): name = forms.CharField(required=False) email = forms.EmailField(label='Your email') comment = forms.CharField(widget=forms.Textarea) def clean(self): # Call clean() method to ensure base class validation super(ContactForm, self).clean() # Get the field values from cleaned_data dict name = self.cleaned_data.get('name','') email = self.cleaned_data.get('email','') # Check if the name is part of the email if name.lower() not in email: # Name is not in email , raise an error raise forms.ValidationError("Please provide an email that contains your name, or viceversa")
In listing 6-16 you can see a
similar approach to the previous clean_<field>()
methods in listing 6-15. But because the clean()
method is a class wide method and you're overriding it yourself, it
differs from clean_<field>()
methods in that you
must first explicitly call the clean()
method of the
base/parent form class (i.e. super(...).clean()
) to
ensure the base class validation is applied. Form field value
extraction is also done through the cleaned_data
data
dictionary, as is the validation logic and raising of
forms.ValidationError
to indicate a rule violation.
Finally, the clean()
method differs from
clean_<field>()
methods in that it doesn't
return a value.
Functionally the
clean()
method is different because it's called after
all the methods in the validators
options and
clean_<field>()
methods, a behavior that's
important because all these methods rely on data in the
cleaned_data
dictionary. This means if a
validators
or clean_<field>()
method raises a ValidationError
error it won't return
any value and the cleaned_data
dictionary won't
contain a value for this field. So by the time the
clean()
method is run, the cleaned_data
dictionary may not necessarily have all the form field values if
one was short-circuited in a validators
or
clean_<field>()
method, which is the reason why
the clean()
method uses the safer dictionary access
syntax cleaned_data.get('<field>','')
to assign
a default value in case the cleaned_data
dictionary
doesn't have a given field.
Another important behavioral
difference between the clean()
method,
clean_<field>()
& validators
methods is how they treat forms.ValidationError
. When
a forms.ValidationError
is raised in a
validators
or clean_<field>()
method, the error is assigned to the <field>
in
question in the form's errors
dictionary -- which is
important for display purposes. But when a
forms.ValidationError
is raised in the
clean()
method, the error is assigned to a special
placeholder field named __all__
-- also known as
"non-field errors" -- which is also placed in the form's
errors
dictionary.
If you want to assign an error in
the clean()
method to a specific form field you can
use the add_error()
method as illustrated in listing
6-17.
Listing 6-17. Django form field error assignment with add_error() in clean() method
def clean(self): # Call clean() method to ensure base class validation super(ContactForm, self).clean() # Get the field values from cleaned_data dict name = self.cleaned_data.get('name','') # Check if the name is part of the email if name.lower() not in email: # Name is not in email , raise an error message = "Please provide an email that contains your name, or viceversa" self.add_error('name', message) self.add_error('email', forms.ValidationError(message)) self.add_error(None, message)
Notice how listing 6-17 uses the
add_error()
method on the form instance instead of the
raise forms.ValidationError()
syntax. The
add_error()
method accepts two arguments, the first
one the form field name on which to assign the error and the second
can be either an error message string or an instance of the
ValidationError
class.
The first two
add_error()
methods in listing 6-17 assign the error
to the name
and email
fields,
respectively. And the third add_error()
method with
the None
key assigns the error to the
__all__
placeholder making it equivalent to
raise forms.ValidationError()
from listing 6-16.
Error form values: errors
Form errors as its been described
in the previous sections are automatically added to a form instance
in the errors
dictionary after calling the
is_valid()
method. Inclusively, it's possible to
access the errors
dictionary directly without calling
the is_valid()
method, unlike the
cleaned_data
dictionary.
The errors
dictionary is important because all form errors end up on it.
Whether the errors are raised because a value doesn't comply with a
form field data type or with raise
forms.ValidationError()
in the clean()
method, clean_<field>()
methods,
validators
methods or the add_error()
method in the clean()
method, all errors end up in a
form's errors
dictionary.
The errors
dictionary follows the pattern
{'<field_name>':'<error_message>'}
which
makes it easy to identify form errors in either a view method or in
a template for display purposes. The only exception to this last
pattern occurs when a form error isn't field specific (e.g. such as
those created in the clean()
method) in which case a
form error is assigned to a special key named __all__
,
errors which are also called non-field errors.
Although you can access the
errors
dictionary as any other Python dictionary,
Django provides a series of methods described in table 6-1 to make
working with errors much easier:
Table 6-1 Django form errors methods
Method | Description |
---|---|
form.errors | Gives you access to the raw errors dictionary. |
form.errors.as_data() | Outputs a dictionary with the original ValidationError instances. For example, if errors outputs {'email':['This field is required']}, then errors.as_data() outputs {'email':[ValidationError(['This field is required'])]}. |
form.errors.as_json(escape_html=False) | Outputs a JSON structure with the contents of the errors dictionary. For example, if errors outputs {'email':['This field is required']}, then errors.as_json() outputs {'email':[{'message':'This field is required','code':'required'}]}. Note that by default as_json() does not escape its output, if you want errors to be escaped use the escape_html flag (e.g. as_json(escape_html=True)). |
form.add_error(field,message) | Associates an error message to a given form field. Although typically used in the clean() method, it can be used in a view method if necessary. Note that if field is not specified the error message goes on to form part of the __all__ placeholder key in errors which are deemed non-field errors. |
form.has_error(field, code=None) | Returns a True or False value if a given field has an error. Note that by default has_error returns True if any error type is associated with a field. To perform the evaluation against a particular error type you can use the code keyword (e.g. form.has_error('email',code='required')). To check if a form has non-field errors you can use NON_FIELD_ERRORS as the field value. |
form.non_field_errors() | Returns a list of non-form errors associated with a form (i.e. the __all__ placeholder key). These errors are typically created in the clean() clean method via ValidationError or add_error(None,'message'). |
You may have noticed the
ValidationError
class instances created in all the
previous examples use different arguments, meaning there are
multiple ways to create ValidationError
instances. For
example, some ValidationError
instances use a simple
string, but it's also possible to create a
ValidationError
instance with a list of
ValidationError
instances, as well as specify a
code
attribute to further classify an error type.
Listing 6-18 illustrates a series of ValidationError
class instances using these variations.
Listing 6-18.- Django form ValidationError instance creation
from django import forms # Placed inside def clean_email(self): raise forms.ValidationError("""Please don't use a hotmail email, we simply don't like it""",code='hotmail') # Placed inside def clean(self): raise forms.ValidationError([ forms.ValidationError(""""Please provide an email that matches your name, or viceversa""",code='custom'), forms.ValidationError("""Please provide your professional email, %(value)s doesn't look professional """,code='required', params={'value':self.cleaned_data.get('email') })
The ValidationError
instance variations presented in listing 6-18 are all optional, but
can become powerful when it comes time to display or filter error
messages on a template, a process that's described in detail in the
'Set up the layout for Django forms in templates' section later in
this chapter.