, RadioSelect is a special case. Each radio button
gets a distinct ID, formed by appending an underscore plus the button's
zero-based index.
>>> f = FrameworkForm(auto_id='id_%s')
>>> print f['language']
When RadioSelect is used with auto_id, and the whole form is printed using
either as_table() or as_ul(), the label for the RadioSelect will point to the
ID of the *first* radio button.
>>> print f
Name:
Language:
>>> print f.as_ul()
Name:
Language:
>>> print f.as_p()
Name:
Language:
MultipleChoiceField is a special case, as its data is required to be a list:
>>> class SongForm(Form):
... name = CharField()
... composers = MultipleChoiceField()
>>> f = SongForm(auto_id=False)
>>> print f['composers']
>>> class SongForm(Form):
... name = CharField()
... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')])
>>> f = SongForm(auto_id=False)
>>> print f['composers']
John Lennon
Paul McCartney
>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False)
>>> print f['name']
>>> print f['composers']
John Lennon
Paul McCartney
MultipleChoiceField rendered as_hidden() is a special case. Because it can
have multiple values, its as_hidden() renders multiple
tags.
>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']}, auto_id=False)
>>> print f['composers'].as_hidden()
>>> f = SongForm({'name': 'From Me To You', 'composers': ['P', 'J']}, auto_id=False)
>>> print f['composers'].as_hidden()
MultipleChoiceField can also be used with the CheckboxSelectMultiple widget.
>>> class SongForm(Form):
... name = CharField()
... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=CheckboxSelectMultiple)
>>> f = SongForm(auto_id=False)
>>> print f['composers']
>>> f = SongForm({'composers': ['J']}, auto_id=False)
>>> print f['composers']
>>> f = SongForm({'composers': ['J', 'P']}, auto_id=False)
>>> print f['composers']
Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox
gets a distinct ID, formed by appending an underscore plus the checkbox's
zero-based index.
>>> f = SongForm(auto_id='%s_id')
>>> print f['composers']
Data for a MultipleChoiceField should be a list. QueryDict and MultiValueDict
conveniently work with this.
>>> data = {'name': 'Yesterday', 'composers': ['J', 'P']}
>>> f = SongForm(data)
>>> f.errors
{}
>>> from django.http import QueryDict
>>> data = QueryDict('name=Yesterday&composers=J&composers=P')
>>> f = SongForm(data)
>>> f.errors
{}
>>> from django.utils.datastructures import MultiValueDict
>>> data = MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P']))
>>> f = SongForm(data)
>>> f.errors
{}
The MultipleHiddenInput widget renders multiple values as hidden fields.
>>> class SongFormHidden(Form):
... name = CharField()
... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=MultipleHiddenInput)
>>> f = SongFormHidden(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])), auto_id=False)
>>> print f.as_ul()
Name:
When using CheckboxSelectMultiple, the framework expects a list of input and
returns a list of input.
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
>>> f.errors['composers']
[u'This field is required.']
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data['composers']
[u'J']
>>> f.cleaned_data['name']
u'Yesterday'
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data['composers']
[u'J', u'P']
>>> f.cleaned_data['name']
u'Yesterday'
Validation errors are HTML-escaped when output as HTML.
>>> class EscapingForm(Form):
... special_name = CharField()
... def clean_special_name(self):
... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name'])
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False)
>>> print f
Special name: Something's wrong with 'Nothing to escape'
>>> f = EscapingForm({'special_name': "Should escape < & > and "}, auto_id=False)
>>> print f
Special name: Something's wrong with 'Should escape < & > and <script>alert('xss')</script>'
""" + \
r""" # [This concatenation is to keep the string below the jython's 32K limit].
# Validating multiple fields in relation to another ###########################
There are a couple of ways to do multiple-field validation. If you want the
validation message to be associated with a particular field, implement the
clean_XXX() method on the Form, where XXX is the field name. As in
Field.clean(), the clean_XXX() method should return the cleaned value. In the
clean_XXX() method, you have access to self.cleaned_data, which is a dictionary
of all the data that has been cleaned *so far*, in order by the fields,
including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean_password2(self):
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
... return self.cleaned_data['password2']
>>> f = UserRegistration(auto_id=False)
>>> f.errors
{}
>>> f = UserRegistration({}, auto_id=False)
>>> f.errors['username']
[u'This field is required.']
>>> f.errors['password1']
[u'This field is required.']
>>> f.errors['password2']
[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
>>> f.errors['password2']
[u'Please make sure your passwords match.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data['username']
u'adrian'
>>> f.cleaned_data['password1']
u'foo'
>>> f.cleaned_data['password2']
u'foo'
Another way of doing multiple-field validation is by implementing the
Form's clean() method. If you do this, any ValidationError raised by that
method will not be associated with a particular field; it will have a
special-case association with the field named '__all__'.
Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of
all the fields/values that have *not* raised a ValidationError. Also note
Form.clean() is required to return a dictionary of all clean data.
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
... return self.cleaned_data
>>> f = UserRegistration(auto_id=False)
>>> f.errors
{}
>>> f = UserRegistration({}, auto_id=False)
>>> print f.as_table()
Username:
Password1:
Password2:
>>> f.errors['username']
[u'This field is required.']
>>> f.errors['password1']
[u'This field is required.']
>>> f.errors['password2']
[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
>>> f.errors['__all__']
[u'Please make sure your passwords match.']
>>> print f.as_table()
Please make sure your passwords match.
Username:
Password1:
Password2:
>>> print f.as_ul()
Please make sure your passwords match.
Username:
Password1:
Password2:
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data['username']
u'adrian'
>>> f.cleaned_data['password1']
u'foo'
>>> f.cleaned_data['password2']
u'foo'
# Dynamic construction ########################################################
It's possible to construct a Form dynamically by adding to the self.fields
dictionary in __init__(). Don't forget to call Form.__init__() within the
subclass' __init__().
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... def __init__(self, *args, **kwargs):
... super(Person, self).__init__(*args, **kwargs)
... self.fields['birthday'] = DateField()
>>> p = Person(auto_id=False)
>>> print p
First name:
Last name:
Birthday:
Instances of a dynamic Form do not persist fields from one Form instance to
the next.
>>> class MyForm(Form):
... def __init__(self, data=None, auto_id=False, field_list=[]):
... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
Field1:
Field2:
>>> field_list = [('field3', CharField()), ('field4', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
Field3:
Field4:
>>> class MyForm(Form):
... default_field_1 = CharField()
... default_field_2 = CharField()
... def __init__(self, data=None, auto_id=False, field_list=[]):
... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
Default field 1:
Default field 2:
Field1:
Field2:
>>> field_list = [('field3', CharField()), ('field4', CharField())]
>>> my_form = MyForm(field_list=field_list)
>>> print my_form
Default field 1:
Default field 2:
Field3:
Field4:
Similarly, changes to field attributes do not persist from one Form instance
to the next.
>>> class Person(Form):
... first_name = CharField(required=False)
... last_name = CharField(required=False)
... def __init__(self, names_required=False, *args, **kwargs):
... super(Person, self).__init__(*args, **kwargs)
... if names_required:
... self.fields['first_name'].required = True
... self.fields['first_name'].widget.attrs['class'] = 'required'
... self.fields['last_name'].required = True
... self.fields['last_name'].widget.attrs['class'] = 'required'
>>> f = Person(names_required=False)
>>> f['first_name'].field.required, f['last_name'].field.required
(False, False)
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
({}, {})
>>> f = Person(names_required=True)
>>> f['first_name'].field.required, f['last_name'].field.required
(True, True)
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
({'class': 'required'}, {'class': 'required'})
>>> f = Person(names_required=False)
>>> f['first_name'].field.required, f['last_name'].field.required
(False, False)
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
({}, {})
>>> class Person(Form):
... first_name = CharField(max_length=30)
... last_name = CharField(max_length=30)
... def __init__(self, name_max_length=None, *args, **kwargs):
... super(Person, self).__init__(*args, **kwargs)
... if name_max_length:
... self.fields['first_name'].max_length = name_max_length
... self.fields['last_name'].max_length = name_max_length
>>> f = Person(name_max_length=None)
>>> f['first_name'].field.max_length, f['last_name'].field.max_length
(30, 30)
>>> f = Person(name_max_length=20)
>>> f['first_name'].field.max_length, f['last_name'].field.max_length
(20, 20)
>>> f = Person(name_max_length=None)
>>> f['first_name'].field.max_length, f['last_name'].field.max_length
(30, 30)
HiddenInput widgets are displayed differently in the as_table(), as_ul()
and as_p() output of a Form -- their verbose names are not displayed, and a
separate row is not displayed. They're displayed in the last row of the
form, directly after that row's form element.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... hidden_text = CharField(widget=HiddenInput)
... birthday = DateField()
>>> p = Person(auto_id=False)
>>> print p
First name:
Last name:
Birthday:
>>> print p.as_ul()
First name:
Last name:
Birthday:
>>> print p.as_p()
First name:
Last name:
Birthday:
With auto_id set, a HiddenInput still gets an ID, but it doesn't get a label.
>>> p = Person(auto_id='id_%s')
>>> print p
First name:
Last name:
Birthday:
>>> print p.as_ul()
First name:
Last name:
Birthday:
>>> print p.as_p()
First name:
Last name:
Birthday:
If a field with a HiddenInput has errors, the as_table() and as_ul() output
will include the error message(s) with the text "(Hidden field [fieldname]) "
prepended. This message is displayed at the top of the output, regardless of
its field's order in the form.
>>> p = Person({'first_name': 'John', 'last_name': 'Lennon', 'birthday': '1940-10-9'}, auto_id=False)
>>> print p
(Hidden field hidden_text) This field is required.
First name:
Last name:
Birthday:
>>> print p.as_ul()
(Hidden field hidden_text) This field is required.
First name:
Last name:
Birthday:
>>> print p.as_p()
(Hidden field hidden_text) This field is required.
First name:
Last name:
Birthday:
A corner case: It's possible for a form to have only HiddenInputs.
>>> class TestForm(Form):
... foo = CharField(widget=HiddenInput)
... bar = CharField(widget=HiddenInput)
>>> p = TestForm(auto_id=False)
>>> print p.as_table()
>>> print p.as_ul()
>>> print p.as_p()
A Form's fields are displayed in the same order in which they were defined.
>>> class TestForm(Form):
... field1 = CharField()
... field2 = CharField()
... field3 = CharField()
... field4 = CharField()
... field5 = CharField()
... field6 = CharField()
... field7 = CharField()
... field8 = CharField()
... field9 = CharField()
... field10 = CharField()
... field11 = CharField()
... field12 = CharField()
... field13 = CharField()
... field14 = CharField()
>>> p = TestForm(auto_id=False)
>>> print p
Field1:
Field2:
Field3:
Field4:
Field5:
Field6:
Field7:
Field8:
Field9:
Field10:
Field11:
Field12:
Field13:
Field14:
Some Field classes have an effect on the HTML attributes of their associated
Widget. If you set max_length in a CharField and its associated widget is
either a TextInput or PasswordInput, then the widget's rendered HTML will
include the "maxlength" attribute.
>>> class UserRegistration(Form):
... username = CharField(max_length=10) # uses TextInput by default
... password = CharField(max_length=10, widget=PasswordInput)
... realname = CharField(max_length=10, widget=TextInput) # redundantly define widget, just to test
... address = CharField() # no max_length defined here
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username:
Password:
Realname:
Address:
If you specify a custom "attrs" that includes the "maxlength" attribute,
the Field's max_length attribute will override whatever "maxlength" you specify
in "attrs".
>>> class UserRegistration(Form):
... username = CharField(max_length=10, widget=TextInput(attrs={'maxlength': 20}))
... password = CharField(max_length=10, widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username:
Password:
# Specifying labels ###########################################################
You can specify the label for a field by using the 'label' argument to a Field
class. If you don't specify 'label', Django will use the field name with
underscores converted to spaces, and the initial letter capitalized.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='Your username')
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput, label='Password (again)')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Your username:
Password1:
Password (again):
Labels for as_* methods will only end in a colon if they don't end in other
punctuation already.
>>> class Questions(Form):
... q1 = CharField(label='The first question')
... q2 = CharField(label='What is your name?')
... q3 = CharField(label='The answer to life is:')
... q4 = CharField(label='Answer this question!')
... q5 = CharField(label='The last question. Period.')
>>> print Questions(auto_id=False).as_p()
The first question:
What is your name?
The answer to life is:
Answer this question!
The last question. Period.
>>> print Questions().as_p()
The first question:
What is your name?
The answer to life is:
Answer this question!
The last question. Period.
A label can be a Unicode object or a bytestring with special characters.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='ŠĐĆŽćžšđ')
... password = CharField(widget=PasswordInput, label=u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111')
>>> p = UserRegistration(auto_id=False)
>>> p.as_ul()
u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: \n\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111: '
If a label is set to the empty string for a field, that field won't get a label.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label='')
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Password:
>>> p = UserRegistration(auto_id='id_%s')
>>> print p.as_ul()
Password:
If label is None, Django will auto-create the label from the field name. This
is default behavior.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, label=None)
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration(auto_id='id_%s')
>>> print p.as_ul()
Username:
Password:
# Label Suffix ################################################################
You can specify the 'label_suffix' argument to a Form class to modify the
punctuation symbol used at the end of a label. By default, the colon (:) is
used, and is only appended to the label if the label doesn't already end with a
punctuation symbol: ., !, ? or :. If you specify a different suffix, it will
be appended regardless of the last character of the label.
>>> class FavoriteForm(Form):
... color = CharField(label='Favorite color?')
... animal = CharField(label='Favorite animal')
...
>>> f = FavoriteForm(auto_id=False)
>>> print f.as_ul()
Favorite color?
Favorite animal:
>>> f = FavoriteForm(auto_id=False, label_suffix='?')
>>> print f.as_ul()
Favorite color?
Favorite animal?
>>> f = FavoriteForm(auto_id=False, label_suffix='')
>>> print f.as_ul()
Favorite color?
Favorite animal
>>> f = FavoriteForm(auto_id=False, label_suffix=u'\u2192')
>>> f.as_ul()
u'Favorite color? \nFavorite animal\u2192 '
""" + \
r""" # [This concatenation is to keep the string below the jython's 32K limit].
# Initial data ################################################################
You can specify initial data for a field by using the 'initial' argument to a
Field class. This initial data is displayed when a Form is rendered with *no*
data. It is not displayed when a Form is rendered with any data (including an
empty dictionary). Also, the initial value is *not* used if data for a
particular required field isn't provided.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, initial='django')
... password = CharField(widget=PasswordInput)
Here, we're not submitting any data, so the initial value will be displayed.
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username:
Password:
Here, we're submitting data, so the initial value will *not* be displayed.
>>> p = UserRegistration({}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u''}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
An 'initial' value is *not* used as a fallback if data is not provided. In this
example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'})
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
# Dynamic initial data ########################################################
The previous technique dealt with "hard-coded" initial data, but it's also
possible to specify initial data after you've already created the Form class
(i.e., at runtime). Use the 'initial' parameter to the Form constructor. This
should be a dictionary containing initial values for one or more fields in the
form, keyed by field name.
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password = CharField(widget=PasswordInput)
Here, we're not submitting any data, so the initial value will be displayed.
>>> p = UserRegistration(initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration(initial={'username': 'stephane'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
The 'initial' parameter is meaningless if you pass data.
>>> p = UserRegistration({}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u''}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u'foo'}, initial={'username': 'django'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
A dynamic 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
then the latter will get precedence.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, initial='django')
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(initial={'username': 'babik'}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
# Callable initial data ########################################################
The previous technique dealt with raw values as initial data, but it's also
possible to specify callable data.
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password = CharField(widget=PasswordInput)
We need to define functions that get called later.
>>> def initial_django():
... return 'django'
>>> def initial_stephane():
... return 'stephane'
Here, we're not submitting any data, so the initial value will be displayed.
>>> p = UserRegistration(initial={'username': initial_django}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
The 'initial' parameter is meaningless if you pass data.
>>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
A callable 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
then the latter will get precedence.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, initial=initial_django)
... password = CharField(widget=PasswordInput)
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username:
Password:
>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False)
>>> print p.as_ul()
Username:
Password:
# Help text ###################################################################
You can specify descriptive text for a field by using the 'help_text' argument
to a Field class. This help text is displayed when a Form is rendered.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text='e.g., user@example.com')
... password = CharField(widget=PasswordInput, help_text='Choose wisely.')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username: e.g., user@example.com
Password: Choose wisely.
>>> print p.as_p()
Username: e.g., user@example.com
Password: Choose wisely.
>>> print p.as_table()
Username: e.g., user@example.com
Password: Choose wisely.
The help text is displayed whether or not data is provided for the form.
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
>>> print p.as_ul()
Username: e.g., user@example.com
Password: Choose wisely.
help_text is not displayed for hidden fields. It can be used for documentation
purposes, though.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text='e.g., user@example.com')
... password = CharField(widget=PasswordInput)
... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
Username: e.g., user@example.com
Password:
Help text can include arbitrary Unicode characters.
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text='ŠĐĆŽćžšđ')
>>> p = UserRegistration(auto_id=False)
>>> p.as_ul()
u'Username: \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111 '
# Subclassing forms ###########################################################
You can subclass a Form to add fields. The resulting form subclass will have
all of the fields of the parent Form, plus whichever fields you define in the
subclass.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
>>> class Musician(Person):
... instrument = CharField()
>>> p = Person(auto_id=False)
>>> print p.as_ul()
First name:
Last name:
Birthday:
>>> m = Musician(auto_id=False)
>>> print m.as_ul()
First name:
Last name:
Birthday:
Instrument:
Yes, you can subclass multiple forms. The fields are added in the order in
which the parent classes are listed.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
>>> class Instrument(Form):
... instrument = CharField()
>>> class Beatle(Person, Instrument):
... haircut_type = CharField()
>>> b = Beatle(auto_id=False)
>>> print b.as_ul()
First name:
Last name:
Birthday:
Instrument:
Haircut type:
# Forms with prefixes #########################################################
Sometimes it's necessary to have multiple forms display on the same HTML page,
or multiple copies of the same form. We can accomplish this with form prefixes.
Pass the keyword argument 'prefix' to the Form constructor to use this feature.
This value will be prepended to each HTML form field name. One way to think
about this is "namespaces for HTML forms". Notice that in the data argument,
each field's key has the prefix, in this case 'person1', prepended to the
actual field name.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
>>> data = {
... 'person1-first_name': u'John',
... 'person1-last_name': u'Lennon',
... 'person1-birthday': u'1940-10-9'
... }
>>> p = Person(data, prefix='person1')
>>> print p.as_ul()
First name:
Last name:
Birthday:
>>> print p['first_name']
>>> print p['last_name']
>>> print p['birthday']
>>> p.errors
{}
>>> p.is_valid()
True
>>> p.cleaned_data['first_name']
u'John'
>>> p.cleaned_data['last_name']
u'Lennon'
>>> p.cleaned_data['birthday']
datetime.date(1940, 10, 9)
Let's try submitting some bad data to make sure form.errors and field.errors
work as expected.
>>> data = {
... 'person1-first_name': u'',
... 'person1-last_name': u'',
... 'person1-birthday': u''
... }
>>> p = Person(data, prefix='person1')
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['last_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
>>> p['first_name'].errors
[u'This field is required.']
>>> p['person1-first_name'].errors
Traceback (most recent call last):
...
KeyError: "Key 'person1-first_name' not found in Form"
In this example, the data doesn't have a prefix, but the form requires it, so
the form doesn't "see" the fields.
>>> data = {
... 'first_name': u'John',
... 'last_name': u'Lennon',
... 'birthday': u'1940-10-9'
... }
>>> p = Person(data, prefix='person1')
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['last_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
With prefixes, a single data dictionary can hold data for multiple instances
of the same form.
>>> data = {
... 'person1-first_name': u'John',
... 'person1-last_name': u'Lennon',
... 'person1-birthday': u'1940-10-9',
... 'person2-first_name': u'Jim',
... 'person2-last_name': u'Morrison',
... 'person2-birthday': u'1943-12-8'
... }
>>> p1 = Person(data, prefix='person1')
>>> p1.is_valid()
True
>>> p1.cleaned_data['first_name']
u'John'
>>> p1.cleaned_data['last_name']
u'Lennon'
>>> p1.cleaned_data['birthday']
datetime.date(1940, 10, 9)
>>> p2 = Person(data, prefix='person2')
>>> p2.is_valid()
True
>>> p2.cleaned_data['first_name']
u'Jim'
>>> p2.cleaned_data['last_name']
u'Morrison'
>>> p2.cleaned_data['birthday']
datetime.date(1943, 12, 8)
By default, forms append a hyphen between the prefix and the field name, but a
form can alter that behavior by implementing the add_prefix() method. This
method takes a field name and returns the prefixed field, according to
self.prefix.
>>> class Person(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
... def add_prefix(self, field_name):
... return self.prefix and '%s-prefix-%s' % (self.prefix, field_name) or field_name
>>> p = Person(prefix='foo')
>>> print p.as_ul()
First name:
Last name:
Birthday:
>>> data = {
... 'foo-prefix-first_name': u'John',
... 'foo-prefix-last_name': u'Lennon',
... 'foo-prefix-birthday': u'1940-10-9'
... }
>>> p = Person(data, prefix='foo')
>>> p.is_valid()
True
>>> p.cleaned_data['first_name']
u'John'
>>> p.cleaned_data['last_name']
u'Lennon'
>>> p.cleaned_data['birthday']
datetime.date(1940, 10, 9)
# Forms with NullBooleanFields ################################################
NullBooleanField is a bit of a special case because its presentation (widget)
is different than its data. This is handled transparently, though.
>>> class Person(Form):
... name = CharField()
... is_cool = NullBooleanField()
>>> p = Person({'name': u'Joe'}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
>>> p = Person({'name': u'Joe', 'is_cool': u'1'}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
>>> p = Person({'name': u'Joe', 'is_cool': u'2'}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
>>> p = Person({'name': u'Joe', 'is_cool': u'3'}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
>>> p = Person({'name': u'Joe', 'is_cool': True}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
>>> p = Person({'name': u'Joe', 'is_cool': False}, auto_id=False)
>>> print p['is_cool']
Unknown
Yes
No
# Forms with FileFields ################################################
FileFields are a special case because they take their data from the request.FILES,
not request.POST.
>>> class FileForm(Form):
... file1 = FileField()
>>> f = FileForm(auto_id=False)
>>> print f
File1:
>>> f = FileForm(data={}, files={}, auto_id=False)
>>> print f
File1:
>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', '')}, auto_id=False)
>>> print f
File1: The submitted file is empty.
>>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False)
>>> print f
File1: No file was submitted. Check the encoding type on the form.
>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('name', 'some content')}, auto_id=False)
>>> print f
File1:
>>> f.is_valid()
True
# Basic form processing in a view #############################################
>>> from django.template import Template, Context
>>> class UserRegistration(Form):
... username = CharField(max_length=10)
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
... return self.cleaned_data
>>> def my_function(method, post_data):
... if method == 'POST':
... form = UserRegistration(post_data, auto_id=False)
... else:
... form = UserRegistration(auto_id=False)
... if form.is_valid():
... return 'VALID: %r' % form.cleaned_data
... t = Template('')
... return t.render(Context({'form': form}))
Case 1: GET (an empty form, with no errors).
>>> print my_function('GET', {})
Case 2: POST with erroneous data (a redisplayed form, with errors).
>>> print my_function('POST', {'username': 'this-is-a-long-username', 'password1': 'foo', 'password2': 'bar'})
Case 3: POST with valid data (the success message).
>>> print my_function('POST', {'username': 'adrian', 'password1': 'secret', 'password2': 'secret'})
VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'}
# Some ideas for using templates with forms ###################################
>>> class UserRegistration(Form):
... username = CharField(max_length=10, help_text="Good luck picking a username that doesn't already exist.")
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
... return self.cleaned_data
You have full flexibility in displaying form fields in a template. Just pass a
Form instance to the template, and use "dot" access to refer to individual
fields. Note, however, that this flexibility comes with the responsibility of
displaying all the errors, including any that might not be associated with a
particular field.
>>> t = Template('''''')
>>> print t.render(Context({'form': UserRegistration(auto_id=False)}))
>>> print t.render(Context({'form': UserRegistration({'username': 'django'}, auto_id=False)}))
Use form.[field].label to output a field's label. You can specify the label for
a field by using the 'label' argument to a Field class. If you don't specify
'label', Django will use the field name with underscores converted to spaces,
and the initial letter capitalized.
>>> t = Template('''''')
>>> print t.render(Context({'form': UserRegistration(auto_id=False)}))
User form.[field].label_tag to output a field's label with a tag
wrapped around it, but *only* if the given field has an "id" attribute.
Recall from above that passing the "auto_id" argument to a Form gives each
field an "id" attribute.
>>> t = Template('''''')
>>> print t.render(Context({'form': UserRegistration(auto_id=False)}))
>>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
User form.[field].help_text to output a field's help text. If the given field
does not have help text, nothing will be output.
>>> t = Template('''''')
>>> print t.render(Context({'form': UserRegistration(auto_id=False)}))
>>> Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)}))
u''
The label_tag() method takes an optional attrs argument: a dictionary of HTML
attributes to add to the tag.
>>> f = UserRegistration(auto_id='id_%s')
>>> for bf in f:
... print bf.label_tag(attrs={'class': 'pretty'})
Username
Password1
Password2
To display the errors that aren't associated with a particular field -- e.g.,
the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
template. If used on its own, it is displayed as a (or an empty string, if
the list of errors is empty). You can also use it in {% if %} statements.
>>> t = Template(''' ''')
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
>>> t = Template('''''')
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
"""