Fixed #22383 -- Added support for HTML5 required attribute on required form fields.

This commit is contained in:
Jon Dufresne 2016-03-28 11:02:04 -07:00 committed by Tim Graham
parent 4d1c229ee5
commit ec6121693f
28 changed files with 849 additions and 659 deletions

View File

@ -85,6 +85,8 @@ class BoundField(object):
widget.is_localized = True widget.is_localized = True
attrs = attrs or {} attrs = attrs or {}
if not widget.is_hidden and self.field.required and self.form.use_required_attribute:
attrs['required'] = True
if self.field.disabled: if self.field.disabled:
attrs['disabled'] = True attrs['disabled'] = True
auto_id = self.auto_id auto_id = self.auto_id

View File

@ -67,10 +67,11 @@ class BaseForm(object):
# class, not to the Form class. # class, not to the Form class.
field_order = None field_order = None
prefix = None prefix = None
use_required_attribute = True
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None, initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None): empty_permitted=False, field_order=None, use_required_attribute=None):
self.is_bound = data is not None or files is not None self.is_bound = data is not None or files is not None
self.data = data or {} self.data = data or {}
self.files = files or {} self.files = files or {}
@ -93,6 +94,9 @@ class BaseForm(object):
self._bound_fields_cache = {} self._bound_fields_cache = {}
self.order_fields(self.field_order if field_order is None else field_order) self.order_fields(self.field_order if field_order is None else field_order)
if use_required_attribute is not None:
self.use_required_attribute = use_required_attribute
def order_fields(self, field_order): def order_fields(self, field_order):
""" """
Rearranges the fields according to field_order. Rearranges the fields according to field_order.

View File

@ -161,6 +161,10 @@ class BaseFormSet(object):
'auto_id': self.auto_id, 'auto_id': self.auto_id,
'prefix': self.add_prefix(i), 'prefix': self.add_prefix(i),
'error_class': self.error_class, 'error_class': self.error_class,
# Don't render the HTML 'required' attribute as it may cause
# incorrect validation for extra, optional, and deleted
# forms in the formset.
'use_required_attribute': False,
} }
if self.is_bound: if self.is_bound:
defaults['data'] = self.data defaults['data'] = self.data
@ -195,6 +199,7 @@ class BaseFormSet(object):
auto_id=self.auto_id, auto_id=self.auto_id,
prefix=self.add_prefix('__prefix__'), prefix=self.add_prefix('__prefix__'),
empty_permitted=True, empty_permitted=True,
use_required_attribute=False,
**self.get_form_kwargs(None) **self.get_form_kwargs(None)
) )
self.add_fields(form, None) self.add_fields(form, None)

View File

@ -278,7 +278,7 @@ class ModelFormMetaclass(DeclarativeFieldsMetaclass):
class BaseModelForm(BaseForm): class BaseModelForm(BaseForm):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None, initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, instance=None): empty_permitted=False, instance=None, use_required_attribute=None):
opts = self._meta opts = self._meta
if opts.model is None: if opts.model is None:
raise ValueError('ModelForm has no model class specified.') raise ValueError('ModelForm has no model class specified.')
@ -296,8 +296,10 @@ class BaseModelForm(BaseForm):
# It is False by default so overriding self.clean() and failing to call # It is False by default so overriding self.clean() and failing to call
# super will stop validate_unique from being called. # super will stop validate_unique from being called.
self._validate_unique = False self._validate_unique = False
super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data, super(BaseModelForm, self).__init__(
error_class, label_suffix, empty_permitted) data, files, auto_id, prefix, object_data, error_class,
label_suffix, empty_permitted, use_required_attribute=use_required_attribute,
)
# Apply ``limit_choices_to`` to each field. # Apply ``limit_choices_to`` to each field.
for field_name in self.fields: for field_name in self.fields:
formfield = self.fields[field_name] formfield = self.fields[field_name]

View File

@ -244,9 +244,9 @@ precedence::
... comment = forms.CharField() ... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False) >>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f) >>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr> <tr><th>Name:</th><td><input type="text" name="name" value="instance" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" /></td></tr> <tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
Checking which form data has changed Checking which form data has changed
==================================== ====================================
@ -305,10 +305,10 @@ You can alter the field of :class:`Form` instance to change the way it is
presented in the form:: presented in the form::
>>> f.as_table().split('\n')[0] >>> f.as_table().split('\n')[0]
'<tr><th>Name:</th><td><input name="name" type="text" value="instance" /></td></tr>' '<tr><th>Name:</th><td><input name="name" type="text" value="instance" required /></td></tr>'
>>> f.fields['name'].label = "Username" >>> f.fields['name'].label = "Username"
>>> f.as_table().split('\n')[0] >>> f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="instance" /></td></tr>' '<tr><th>Username:</th><td><input name="name" type="text" value="instance" required /></td></tr>'
Beware not to alter the ``base_fields`` attribute because this modification Beware not to alter the ``base_fields`` attribute because this modification
will influence all subsequent ``ContactForm`` instances within the same Python will influence all subsequent ``ContactForm`` instances within the same Python
@ -317,7 +317,7 @@ process::
>>> f.base_fields['name'].label = "Username" >>> f.base_fields['name'].label = "Username"
>>> another_f = CommentForm(auto_id=False) >>> another_f = CommentForm(auto_id=False)
>>> another_f.as_table().split('\n')[0] >>> another_f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="class" /></td></tr>' '<tr><th>Username:</th><td><input name="name" type="text" value="class" required /></td></tr>'
Accessing "clean" data Accessing "clean" data
====================== ======================
@ -421,9 +421,9 @@ simply ``print`` it::
>>> f = ContactForm() >>> f = ContactForm()
>>> print(f) >>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr> <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
If the form is bound to data, the HTML output will include that data If the form is bound to data, the HTML output will include that data
@ -438,9 +438,9 @@ include ``checked="checked"`` if appropriate::
... 'cc_myself': True} ... 'cc_myself': True}
>>> f = ContactForm(data) >>> f = ContactForm(data)
>>> print(f) >>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr> <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" required /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr> <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" required /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" /></td></tr> <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" required /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
This default output is a two-column HTML table, with a ``<tr>`` for each field. This default output is a two-column HTML table, with a ``<tr>`` for each field.
@ -485,11 +485,11 @@ containing one field::
>>> f = ContactForm() >>> f = ContactForm()
>>> f.as_p() >>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>' '<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>'
>>> print(f.as_p()) >>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p> <p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p> <p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></p> <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p> <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
``as_ul()`` ``as_ul()``
@ -504,11 +504,11 @@ flexibility::
>>> f = ContactForm() >>> f = ContactForm()
>>> f.as_ul() >>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>' '<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li> <li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li> <li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></li> <li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required /></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li> <li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
``as_table()`` ``as_table()``
@ -522,11 +522,11 @@ it calls its ``as_table()`` method behind the scenes::
>>> f = ContactForm() >>> f = ContactForm()
>>> f.as_table() >>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>' '<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr> <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
.. _ref-forms-api-styling-form-rows: .. _ref-forms-api-styling-form-rows:
@ -597,19 +597,19 @@ tags nor ``id`` attributes::
>>> f = ContactForm(auto_id=False) >>> f = ContactForm(auto_id=False)
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr> <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr> <tr><th>Message:</th><td><input type="text" name="message" required /></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" /></td></tr> <tr><th>Sender:</th><td><input type="email" name="sender" required /></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul()) >>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li> <li>Subject: <input type="text" name="subject" maxlength="100" required /></li>
<li>Message: <input type="text" name="message" /></li> <li>Message: <input type="text" name="message" required /></li>
<li>Sender: <input type="email" name="sender" /></li> <li>Sender: <input type="email" name="sender" required /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li> <li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p()) >>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /></p> <p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
<p>Message: <input type="text" name="message" /></p> <p>Message: <input type="text" name="message" required /></p>
<p>Sender: <input type="email" name="sender" /></p> <p>Sender: <input type="email" name="sender" required /></p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
If ``auto_id`` is set to ``True``, then the form output *will* include If ``auto_id`` is set to ``True``, then the form output *will* include
@ -618,19 +618,19 @@ field::
>>> f = ContactForm(auto_id=True) >>> f = ContactForm(auto_id=True)
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr> <tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr> <tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" required /></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" /></td></tr> <tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" required /></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr> <tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li> <li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required /></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li> <li><label for="message">Message:</label> <input type="text" name="message" id="message" required /></li>
<li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></li> <li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required /></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li> <li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
>>> print(f.as_p()) >>> print(f.as_p())
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p> <p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p> <p><label for="message">Message:</label> <input type="text" name="message" id="message" required /></p>
<p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></p> <p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required /></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p> <p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
If ``auto_id`` is set to a string containing the format character ``'%s'``, If ``auto_id`` is set to a string containing the format character ``'%s'``,
@ -641,19 +641,19 @@ attributes based on the format string. For example, for a format string
>>> f = ContactForm(auto_id='id_for_%s') >>> f = ContactForm(auto_id='id_for_%s')
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr> <tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr> <tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" required /></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" /></td></tr> <tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" required /></td></tr>
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr> <tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> <li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required /></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li> <li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required /></li>
<li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></li> <li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required /></li>
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> <li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> print(f.as_p()) >>> print(f.as_p())
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p> <p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p> <p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required /></p>
<p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></p> <p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required /></p>
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p> <p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
If ``auto_id`` is set to any other true value -- such as a string that doesn't If ``auto_id`` is set to any other true value -- such as a string that doesn't
@ -671,15 +671,15 @@ It's possible to customize that character, or omit it entirely, using the
>>> f = ContactForm(auto_id='id_for_%s', label_suffix='') >>> f = ContactForm(auto_id='id_for_%s', label_suffix='')
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> <li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required /></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li> <li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" required /></li>
<li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" /></li> <li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" required /></li>
<li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> <li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->') >>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->')
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> <li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required /></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li> <li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" required /></li>
<li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" /></li> <li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" required /></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> <li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
Note that the label suffix is added only if the last character of the Note that the label suffix is added only if the last character of the
@ -692,6 +692,17 @@ This will take precedence over :attr:`Form.label_suffix
using the ``label_suffix`` parameter to using the ``label_suffix`` parameter to
:meth:`~django.forms.BoundField.label_tag`. :meth:`~django.forms.BoundField.label_tag`.
.. attribute:: Form.use_required_attribute
.. versionadded:: 1.10
When set to ``True`` (the default), required form fields will have the
``required`` HTML attribute.
:doc:`Formsets </topics/forms/formsets>` instantiate forms with
``use_required_attribute=False`` to avoid incorrect browser validation when
adding and deleting forms from a formset.
Notes on field ordering Notes on field ordering
----------------------- -----------------------
@ -741,21 +752,21 @@ method you're using::
... 'cc_myself': True} ... 'cc_myself': True}
>>> f = ContactForm(data, auto_id=False) >>> f = ContactForm(data, auto_id=False)
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr> <tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr> <tr><th>Message:</th><td><input type="text" name="message" value="Hi there" required /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" /></td></tr> <tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr> <tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul()) >>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li> <li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" required /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li> <li>Message: <input type="text" name="message" value="Hi there" required /></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" /></li> <li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li> <li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p()) >>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p> <p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p> <p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p> <p>Message: <input type="text" name="message" value="Hi there" required /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p> <p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" /></p> <p>Sender: <input type="email" name="sender" value="invalid email address" required /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
.. _ref-forms-error-list-format: .. _ref-forms-error-list-format:
@ -778,10 +789,10 @@ Python 2)::
>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList) >>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
>>> f.as_p() >>> f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div> <div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p> <p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p> <p>Message: <input type="text" name="message" value="Hi there" required /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div> <div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="email" name="sender" value="invalid email address" /></p> <p>Sender: <input type="email" name="sender" value="invalid email address" required /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
More granular output More granular output
@ -803,25 +814,25 @@ using the field's name as the key::
>>> form = ContactForm() >>> form = ContactForm()
>>> print(form['subject']) >>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" /> <input id="id_subject" type="text" name="subject" maxlength="100" required />
To retrieve all ``BoundField`` objects, iterate the form:: To retrieve all ``BoundField`` objects, iterate the form::
>>> form = ContactForm() >>> form = ContactForm()
>>> for boundfield in form: print(boundfield) >>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" /> <input id="id_subject" type="text" name="subject" maxlength="100" required />
<input type="text" name="message" id="id_message" /> <input type="text" name="message" id="id_message" required />
<input type="email" name="sender" id="id_sender" /> <input type="email" name="sender" id="id_sender" required />
<input type="checkbox" name="cc_myself" id="id_cc_myself" /> <input type="checkbox" name="cc_myself" id="id_cc_myself" />
The field-specific output honors the form object's ``auto_id`` setting:: The field-specific output honors the form object's ``auto_id`` setting::
>>> f = ContactForm(auto_id=False) >>> f = ContactForm(auto_id=False)
>>> print(f['message']) >>> print(f['message'])
<input type="text" name="message" /> <input type="text" name="message" required />
>>> f = ContactForm(auto_id='id_%s') >>> f = ContactForm(auto_id='id_%s')
>>> print(f['message']) >>> print(f['message'])
<input type="text" name="message" id="id_message" /> <input type="text" name="message" id="id_message" required />
Attributes of ``BoundField`` Attributes of ``BoundField``
---------------------------- ----------------------------
@ -852,7 +863,7 @@ Attributes of ``BoundField``
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} >>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False) >>> f = ContactForm(data, auto_id=False)
>>> print(f['message']) >>> print(f['message'])
<input type="text" name="message" /> <input type="text" name="message" required />
>>> f['message'].errors >>> f['message'].errors
['This field is required.'] ['This field is required.']
>>> print(f['message'].errors) >>> print(f['message'].errors)
@ -904,7 +915,7 @@ Attributes of ``BoundField``
.. code-block:: html .. code-block:: html
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" /> <label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required />
.. attribute:: BoundField.is_hidden .. attribute:: BoundField.is_hidden
@ -1125,11 +1136,11 @@ fields are ordered first::
... priority = forms.CharField() ... priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False) >>> f = ContactFormWithPriority(auto_id=False)
>>> print(f.as_ul()) >>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li> <li>Subject: <input type="text" name="subject" maxlength="100" required /></li>
<li>Message: <input type="text" name="message" /></li> <li>Message: <input type="text" name="message" required /></li>
<li>Sender: <input type="email" name="sender" /></li> <li>Sender: <input type="email" name="sender" required /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li> <li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li> <li>Priority: <input type="text" name="priority" required /></li>
It's possible to subclass multiple forms, treating forms as mixins. In this It's possible to subclass multiple forms, treating forms as mixins. In this
example, ``BeatleForm`` subclasses both ``PersonForm`` and ``InstrumentForm`` example, ``BeatleForm`` subclasses both ``PersonForm`` and ``InstrumentForm``
@ -1146,10 +1157,10 @@ classes::
... haircut_type = CharField() ... haircut_type = CharField()
>>> b = BeatleForm(auto_id=False) >>> b = BeatleForm(auto_id=False)
>>> print(b.as_ul()) >>> print(b.as_ul())
<li>First name: <input type="text" name="first_name" /></li> <li>First name: <input type="text" name="first_name" required /></li>
<li>Last name: <input type="text" name="last_name" /></li> <li>Last name: <input type="text" name="last_name" required /></li>
<li>Instrument: <input type="text" name="instrument" /></li> <li>Instrument: <input type="text" name="instrument" required /></li>
<li>Haircut type: <input type="text" name="haircut_type" /></li> <li>Haircut type: <input type="text" name="haircut_type" required /></li>
It's possible to declaratively remove a ``Field`` inherited from a parent class It's possible to declaratively remove a ``Field`` inherited from a parent class
by setting the name of the field to ``None`` on the subclass. For example:: by setting the name of the field to ``None`` on the subclass. For example::
@ -1179,11 +1190,11 @@ You can put several Django forms inside one ``<form>`` tag. To give each
>>> mother = PersonForm(prefix="mother") >>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father") >>> father = PersonForm(prefix="father")
>>> print(mother.as_ul()) >>> print(mother.as_ul())
<li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" /></li> <li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" required /></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></li> <li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" required /></li>
>>> print(father.as_ul()) >>> print(father.as_ul())
<li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li> <li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" required /></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li> <li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" required /></li>
The prefix can also be specified on the form class:: The prefix can also be specified on the form class::

View File

@ -115,9 +115,9 @@ We've specified ``auto_id=False`` to simplify the output::
... comment = forms.CharField() ... comment = forms.CharField()
>>> f = CommentForm(auto_id=False) >>> f = CommentForm(auto_id=False)
>>> print(f) >>> print(f)
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr> <tr><th>Your name:</th><td><input type="text" name="name" required /></td></tr>
<tr><th>Your website:</th><td><input type="url" name="url" /></td></tr> <tr><th>Your website:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
``label_suffix`` ``label_suffix``
---------------- ----------------
@ -133,9 +133,9 @@ The ``label_suffix`` argument lets you override the form's
... captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =') ... captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =')
>>> f = ContactForm(label_suffix='?') >>> f = ContactForm(label_suffix='?')
>>> print(f.as_p()) >>> print(f.as_p())
<p><label for="id_age">Age?</label> <input id="id_age" name="age" type="number" /></p> <p><label for="id_age">Age?</label> <input id="id_age" name="age" type="number" required /></p>
<p><label for="id_nationality">Nationality?</label> <input id="id_nationality" name="nationality" type="text" /></p> <p><label for="id_nationality">Nationality?</label> <input id="id_nationality" name="nationality" type="text" required /></p>
<p><label for="id_captcha_answer">2 + 2 =</label> <input id="id_captcha_answer" name="captcha_answer" type="number" /></p> <p><label for="id_captcha_answer">2 + 2 =</label> <input id="id_captcha_answer" name="captcha_answer" type="number" required /></p>
``initial`` ``initial``
----------- -----------
@ -157,9 +157,9 @@ field is initialized to a particular value. For example::
... comment = forms.CharField() ... comment = forms.CharField()
>>> f = CommentForm(auto_id=False) >>> f = CommentForm(auto_id=False)
>>> print(f) >>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> <tr><th>Name:</th><td><input type="text" name="name" value="Your name" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" value="http://" /></td></tr> <tr><th>Url:</th><td><input type="url" name="url" value="http://" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
You may be thinking, why not just pass a dictionary of the initial values as You may be thinking, why not just pass a dictionary of the initial values as
data when displaying the form? Well, if you do that, you'll trigger validation, data when displaying the form? Well, if you do that, you'll trigger validation,
@ -172,9 +172,9 @@ and the HTML output will include any validation errors::
>>> default_data = {'name': 'Your name', 'url': 'http://'} >>> default_data = {'name': 'Your name', 'url': 'http://'}
>>> f = CommentForm(default_data, auto_id=False) >>> f = CommentForm(default_data, auto_id=False)
>>> print(f) >>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> <tr><th>Name:</th><td><input type="text" name="name" value="Your name" required /></td></tr>
<tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="url" name="url" value="http://" /></td></tr> <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="url" name="url" value="http://" required /></td></tr>
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr> <tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" required /></td></tr>
This is why ``initial`` values are only displayed for unbound forms. For bound This is why ``initial`` values are only displayed for unbound forms. For bound
forms, the HTML output will use the bound data. forms, the HTML output will use the bound data.
@ -201,7 +201,7 @@ Instead of a constant, you can also pass any callable::
>>> class DateForm(forms.Form): >>> class DateForm(forms.Form):
... day = forms.DateField(initial=datetime.date.today) ... day = forms.DateField(initial=datetime.date.today)
>>> print(DateForm()) >>> print(DateForm())
<tr><th>Day:</th><td><input type="text" name="day" value="12/23/2008" /><td></tr> <tr><th>Day:</th><td><input type="text" name="day" value="12/23/2008" required /><td></tr>
The callable will be evaluated only when the unbound form is displayed, not when it is defined. The callable will be evaluated only when the unbound form is displayed, not when it is defined.
@ -237,19 +237,19 @@ fields. We've specified ``auto_id=False`` to simplify the output::
... cc_myself = forms.BooleanField(required=False) ... cc_myself = forms.BooleanField(required=False)
>>> f = HelpTextContactForm(auto_id=False) >>> f = HelpTextContactForm(auto_id=False)
>>> print(f.as_table()) >>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br /><span class="helptext">100 characters max.</span></td></tr> <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required /><br /><span class="helptext">100 characters max.</span></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr> <tr><th>Message:</th><td><input type="text" name="message" required /></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" /><br />A valid email address, please.</td></tr> <tr><th>Sender:</th><td><input type="email" name="sender" required /><br />A valid email address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())) >>> print(f.as_ul()))
<li>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></li> <li>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></li>
<li>Message: <input type="text" name="message" /></li> <li>Message: <input type="text" name="message" required /></li>
<li>Sender: <input type="email" name="sender" /> A valid email address, please.</li> <li>Sender: <input type="email" name="sender" required /> A valid email address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li> <li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p()) >>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></p> <p>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></p>
<p>Message: <input type="text" name="message" /></p> <p>Message: <input type="text" name="message" required /></p>
<p>Sender: <input type="email" name="sender" /> A valid email address, please.</p> <p>Sender: <input type="email" name="sender" required /> A valid email address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
``error_messages`` ``error_messages``

View File

@ -135,9 +135,9 @@ provided for each widget will be rendered exactly the same::
>>> f = CommentForm(auto_id=False) >>> f = CommentForm(auto_id=False)
>>> f.as_table() >>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" /></td></tr> <tr><th>Name:</th><td><input type="text" name="name" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url"/></td></tr> <tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
On a real Web page, you probably don't want every widget to look the same. You On a real Web page, you probably don't want every widget to look the same. You
might want a larger input element for the comment, and you might want the might want a larger input element for the comment, and you might want the
@ -154,9 +154,9 @@ Django will then include the extra attributes in the rendered output:
>>> f = CommentForm(auto_id=False) >>> f = CommentForm(auto_id=False)
>>> f.as_table() >>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr> <tr><th>Name:</th><td><input type="text" name="name" class="special" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url"/></td></tr> <tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" size="40" required /></td></tr>
You can also set the HTML ``id`` using :attr:`~Widget.attrs`. See You can also set the HTML ``id`` using :attr:`~Widget.attrs`. See
:attr:`BoundField.id_for_label` for an example. :attr:`BoundField.id_for_label` for an example.
@ -204,7 +204,7 @@ foundation for custom widgets.
>>> from django import forms >>> from django import forms
>>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',}) >>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',})
>>> name.render('name', 'A name') >>> name.render('name', 'A name')
'<input title="Your name" type="text" name="name" value="A name" size="10" />' '<input title="Your name" type="text" name="name" value="A name" size="10" required />'
If you assign a value of ``True`` or ``False`` to an attribute, If you assign a value of ``True`` or ``False`` to an attribute,
it will be rendered as an HTML5 boolean attribute:: it will be rendered as an HTML5 boolean attribute::
@ -627,16 +627,16 @@ Selector and checkbox widgets
.. code-block:: html .. code-block:: html
<div class="myradio"> <div class="myradio">
<label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" /> John</label> <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /> John</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /> Paul</label> <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /> Paul</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" /> George</label> <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /> George</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /> Ringo</label> <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /> Ringo</label>
</div> </div>
That included the ``<label>`` tags. To get more granular, you can use each That included the ``<label>`` tags. To get more granular, you can use each
@ -658,22 +658,22 @@ Selector and checkbox widgets
<label for="id_beatles_0"> <label for="id_beatles_0">
John John
<span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" /></span> <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /></span>
</label> </label>
<label for="id_beatles_1"> <label for="id_beatles_1">
Paul Paul
<span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /></span> <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /></span>
</label> </label>
<label for="id_beatles_2"> <label for="id_beatles_2">
George George
<span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" /></span> <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /></span>
</label> </label>
<label for="id_beatles_3"> <label for="id_beatles_3">
Ringo Ringo
<span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /></span> <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /></span>
</label> </label>
If you decide not to loop over the radio buttons -- e.g., if your template If you decide not to loop over the radio buttons -- e.g., if your template

View File

@ -259,6 +259,12 @@ Forms
* The ``<input>`` tag rendered by :class:`~django.forms.CharField` now includes * The ``<input>`` tag rendered by :class:`~django.forms.CharField` now includes
a ``minlength`` attribute if the field has a ``min_length``. a ``minlength`` attribute if the field has a ``min_length``.
* Required form fields now have the ``required`` HTML attribute. Set the new
:attr:`Form.use_required_attribute <django.forms.Form.use_required_attribute>`
attribute to ``False`` to disable it. The ``required`` attribute isn't
included on forms of formsets because the browser validation may not be
correct when adding and deleting formsets.
Generic Views Generic Views
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -760,6 +766,11 @@ Miscellaneous
:attr:`ModelAdmin.save_as_continue :attr:`ModelAdmin.save_as_continue
<django.contrib.admin.ModelAdmin.save_as_continue>` attribute to ``False``. <django.contrib.admin.ModelAdmin.save_as_continue>` attribute to ``False``.
* Required form fields now have the ``required`` HTML attribute. Set the
:attr:`Form.use_required_attribute <django.forms.Form.use_required_attribute>`
attribute to ``False`` to disable it. You could also add the ``novalidate``
attribute to ``<form>`` if you don't want browser validation.
.. _deprecated-features-1.10: .. _deprecated-features-1.10:
Features deprecated in 1.10 Features deprecated in 1.10

View File

@ -164,6 +164,11 @@ As we can see, ``formset.errors`` is a list whose entries correspond to the
forms in the formset. Validation was performed for each of the two forms, and forms in the formset. Validation was performed for each of the two forms, and
the expected error message appears for the second item. the expected error message appears for the second item.
Just like when using a normal ``Form``, each form in the formset may include
HTML attributes such as ``maxlength`` for browser validation. However, forms of
formsets won't include the ``required`` attribute as that validation may be
incorrect when adding and deleting forms.
.. method:: BaseFormSet.total_error_count() .. method:: BaseFormSet.total_error_count()
To check how many errors there are in the formset, we can use the To check how many errors there are in the formset, we can use the

View File

@ -259,7 +259,7 @@ The whole form, when rendered for the first time, will look like:
.. code-block:: html+django .. code-block:: html+django
<label for="your_name">Your name: </label> <label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100"> <input id="your_name" type="text" name="your_name" maxlength="100" required />
Note that it **does not** include the ``<form>`` tags, or a submit button. Note that it **does not** include the ``<form>`` tags, or a submit button.
We'll have to provide those ourselves in the template. We'll have to provide those ourselves in the template.
@ -512,11 +512,11 @@ Here's the output of ``{{ form.as_p }}`` for our ``ContactForm`` instance:
.. code-block:: html+django .. code-block:: html+django
<p><label for="id_subject">Subject:</label> <p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" /></p> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label> <p><label for="id_message">Message:</label>
<textarea name="message" id="id_message"></textarea></p> <textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label> <p><label for="id_sender">Sender:</label>
<input type="email" name="sender" id="id_sender" /></p> <input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label> <p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>

View File

@ -3291,7 +3291,7 @@ class AdminActionsTest(TestCase):
Refs #15964. Refs #15964.
""" """
response = self.client.get(reverse('admin:admin_views_externalsubscriber_changelist')) response = self.client.get(reverse('admin:admin_views_externalsubscriber_changelist'))
self.assertContains(response, '''<label>Action: <select name="action"> self.assertContains(response, '''<label>Action: <select name="action" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="delete_selected">Delete selected external <option value="delete_selected">Delete selected external
subscribers</option> subscribers</option>

View File

@ -119,4 +119,4 @@ class CharFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_charfield_disabled(self): def test_charfield_disabled(self):
f = CharField(disabled=True) f = CharField(disabled=True)
self.assertWidgetRendersTo(f, '<input type="text" name="f" id="id_f" disabled />') self.assertWidgetRendersTo(f, '<input type="text" name="f" id="id_f" disabled required />')

View File

@ -81,6 +81,6 @@ class ChoiceFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')], disabled=True) f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')], disabled=True)
self.assertWidgetRendersTo( self.assertWidgetRendersTo(
f, f,
'<select id="id_f" name="f" disabled><option value="J">John</option>' '<select id="id_f" name="f" disabled required><option value="J">John</option>'
'<option value="P">Paul</option></select>' '<option value="P">Paul</option></select>'
) )

View File

@ -14,7 +14,7 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_decimalfield_1(self): def test_decimalfield_1(self):
f = DecimalField(max_digits=4, decimal_places=2) f = DecimalField(max_digits=4, decimal_places=2)
self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" />') self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean('') f.clean('')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
@ -80,7 +80,10 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
max_value=decimal.Decimal('1.5'), max_value=decimal.Decimal('1.5'),
min_value=decimal.Decimal('0.5') min_value=decimal.Decimal('0.5')
) )
self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" type="number" id="id_f" />') self.assertWidgetRendersTo(
f,
'<input step="0.01" name="f" min="0.5" max="1.5" type="number" id="id_f" required />',
)
with self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'"):
f.clean('1.6') f.clean('1.6')
with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'"):
@ -136,7 +139,7 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
f = DecimalField(max_digits=20) f = DecimalField(max_digits=20)
self.assertEqual(f.widget_attrs(NumberInput()), {'step': 'any'}) self.assertEqual(f.widget_attrs(NumberInput()), {'step': 'any'})
f = DecimalField(max_digits=6, widget=NumberInput(attrs={'step': '0.01'})) f = DecimalField(max_digits=6, widget=NumberInput(attrs={'step': '0.01'}))
self.assertWidgetRendersTo(f, '<input step="0.01" name="f" type="number" id="id_f" />') self.assertWidgetRendersTo(f, '<input step="0.01" name="f" type="number" id="id_f" required />')
def test_decimalfield_localized(self): def test_decimalfield_localized(self):
""" """
@ -144,7 +147,7 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
number input specific attributes. number input specific attributes.
""" """
f = DecimalField(localize=True) f = DecimalField(localize=True)
self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" required />')
def test_decimalfield_changed(self): def test_decimalfield_changed(self):
f = DecimalField(max_digits=2, decimal_places=2) f = DecimalField(max_digits=2, decimal_places=2)

View File

@ -24,7 +24,7 @@ class DurationFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_durationfield_render(self): def test_durationfield_render(self):
self.assertWidgetRendersTo( self.assertWidgetRendersTo(
DurationField(initial=datetime.timedelta(hours=1)), DurationField(initial=datetime.timedelta(hours=1)),
'<input id="id_f" type="text" name="f" value="01:00:00">', '<input id="id_f" type="text" name="f" value="01:00:00" required>',
) )
def test_durationfield_integer_value(self): def test_durationfield_integer_value(self):

View File

@ -11,7 +11,7 @@ class EmailFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_emailfield_1(self): def test_emailfield_1(self):
f = EmailField() f = EmailField()
self.assertWidgetRendersTo(f, '<input type="email" name="f" id="id_f" />') self.assertWidgetRendersTo(f, '<input type="email" name="f" id="id_f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean('') f.clean('')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
@ -42,7 +42,10 @@ class EmailFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_emailfield_min_max_length(self): def test_emailfield_min_max_length(self):
f = EmailField(min_length=10, max_length=15) f = EmailField(min_length=10, max_length=15)
self.assertWidgetRendersTo(f, '<input id="id_f" type="email" name="f" maxlength="15" minlength="10" />') self.assertWidgetRendersTo(
f,
'<input id="id_f" type="email" name="f" maxlength="15" minlength="10" required />',
)
with self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 9).'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 10 characters (it has 9).'"):
f.clean('a@foo.com') f.clean('a@foo.com')
self.assertEqual('alf@foo.com', f.clean('alf@foo.com')) self.assertEqual('alf@foo.com', f.clean('alf@foo.com'))

View File

@ -11,7 +11,7 @@ class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_floatfield_1(self): def test_floatfield_1(self):
f = FloatField() f = FloatField()
self.assertWidgetRendersTo(f, '<input step="any" type="number" name="f" id="id_f" />') self.assertWidgetRendersTo(f, '<input step="any" type="number" name="f" id="id_f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean('') f.clean('')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
@ -48,7 +48,10 @@ class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_floatfield_3(self): def test_floatfield_3(self):
f = FloatField(max_value=1.5, min_value=0.5) f = FloatField(max_value=1.5, min_value=0.5)
self.assertWidgetRendersTo(f, '<input step="any" name="f" min="0.5" max="1.5" type="number" id="id_f" />') self.assertWidgetRendersTo(
f,
'<input step="any" name="f" min="0.5" max="1.5" type="number" id="id_f" required />',
)
with self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'"):
f.clean('1.6') f.clean('1.6')
with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'"):
@ -60,7 +63,10 @@ class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_floatfield_widget_attrs(self): def test_floatfield_widget_attrs(self):
f = FloatField(widget=NumberInput(attrs={'step': 0.01, 'max': 1.0, 'min': 0.0})) f = FloatField(widget=NumberInput(attrs={'step': 0.01, 'max': 1.0, 'min': 0.0}))
self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.0" max="1.0" type="number" id="id_f" />') self.assertWidgetRendersTo(
f,
'<input step="0.01" name="f" min="0.0" max="1.0" type="number" id="id_f" required />',
)
def test_floatfield_localized(self): def test_floatfield_localized(self):
""" """
@ -68,7 +74,7 @@ class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
number input specific attributes. number input specific attributes.
""" """
f = FloatField(localize=True) f = FloatField(localize=True)
self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" />') self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" required />')
def test_floatfield_changed(self): def test_floatfield_changed(self):
f = FloatField() f = FloatField()

View File

@ -11,7 +11,7 @@ class IntegerFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_integerfield_1(self): def test_integerfield_1(self):
f = IntegerField() f = IntegerField()
self.assertWidgetRendersTo(f, '<input type="number" name="f" id="id_f" />') self.assertWidgetRendersTo(f, '<input type="number" name="f" id="id_f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean('') f.clean('')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
@ -53,7 +53,7 @@ class IntegerFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_integerfield_3(self): def test_integerfield_3(self):
f = IntegerField(max_value=10) f = IntegerField(max_value=10)
self.assertWidgetRendersTo(f, '<input max="10" type="number" name="f" id="id_f" />') self.assertWidgetRendersTo(f, '<input max="10" type="number" name="f" id="id_f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean(None) f.clean(None)
self.assertEqual(1, f.clean(1)) self.assertEqual(1, f.clean(1))
@ -68,7 +68,7 @@ class IntegerFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_integerfield_4(self): def test_integerfield_4(self):
f = IntegerField(min_value=10) f = IntegerField(min_value=10)
self.assertWidgetRendersTo(f, '<input id="id_f" type="number" name="f" min="10" />') self.assertWidgetRendersTo(f, '<input id="id_f" type="number" name="f" min="10" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean(None) f.clean(None)
with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'"):
@ -82,7 +82,7 @@ class IntegerFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_integerfield_5(self): def test_integerfield_5(self):
f = IntegerField(min_value=10, max_value=20) f = IntegerField(min_value=10, max_value=20)
self.assertWidgetRendersTo(f, '<input id="id_f" max="20" type="number" name="f" min="10" />') self.assertWidgetRendersTo(f, '<input id="id_f" max="20" type="number" name="f" min="10" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean(None) f.clean(None)
with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 10.'"):
@ -103,7 +103,7 @@ class IntegerFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
number input specific attributes. number input specific attributes.
""" """
f1 = IntegerField(localize=True) f1 = IntegerField(localize=True)
self.assertWidgetRendersTo(f1, '<input id="id_f" name="f" type="text" />') self.assertWidgetRendersTo(f1, '<input id="id_f" name="f" type="text" required />')
def test_integerfield_float(self): def test_integerfield_float(self):
f = IntegerField() f = IntegerField()

View File

@ -112,15 +112,15 @@ class MultiValueFieldTest(SimpleTestCase):
form.as_table(), form.as_table(),
""" """
<tr><th><label for="id_field1_0">Field1:</label></th> <tr><th><label for="id_field1_0">Field1:</label></th>
<td><input type="text" name="field1_0" id="id_field1_0" /> <td><input type="text" name="field1_0" id="id_field1_0" required />
<select multiple="multiple" name="field1_1" id="id_field1_1"> <select multiple="multiple" name="field1_1" id="id_field1_1" required>
<option value="J">John</option> <option value="J">John</option>
<option value="P">Paul</option> <option value="P">Paul</option>
<option value="G">George</option> <option value="G">George</option>
<option value="R">Ringo</option> <option value="R">Ringo</option>
</select> </select>
<input type="text" name="field1_2_0" id="id_field1_2_0" /> <input type="text" name="field1_2_0" id="id_field1_2_0" required />
<input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr> <input type="text" name="field1_2_1" id="id_field1_2_1" required /></td></tr>
""", """,
) )
@ -135,15 +135,15 @@ class MultiValueFieldTest(SimpleTestCase):
form.as_table(), form.as_table(),
""" """
<tr><th><label for="id_field1_0">Field1:</label></th> <tr><th><label for="id_field1_0">Field1:</label></th>
<td><input type="text" name="field1_0" value="some text" id="id_field1_0" /> <td><input type="text" name="field1_0" value="some text" id="id_field1_0" required />
<select multiple="multiple" name="field1_1" id="id_field1_1"> <select multiple="multiple" name="field1_1" id="id_field1_1" required>
<option value="J" selected="selected">John</option> <option value="J" selected="selected">John</option>
<option value="P" selected="selected">Paul</option> <option value="P" selected="selected">Paul</option>
<option value="G">George</option> <option value="G">George</option>
<option value="R">Ringo</option> <option value="R">Ringo</option>
</select> </select>
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /> <input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" required />
<input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> <input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" required /></td></tr>
""", """,
) )

View File

@ -11,7 +11,7 @@ class URLFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_urlfield_1(self): def test_urlfield_1(self):
f = URLField() f = URLField()
self.assertWidgetRendersTo(f, '<input type="url" name="f" id="id_f" />') self.assertWidgetRendersTo(f, '<input type="url" name="f" id="id_f" required />')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean('') f.clean('')
with self.assertRaisesMessage(ValidationError, "'This field is required.'"): with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
@ -91,7 +91,7 @@ class URLFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_urlfield_5(self): def test_urlfield_5(self):
f = URLField(min_length=15, max_length=20) f = URLField(min_length=15, max_length=20)
self.assertWidgetRendersTo(f, '<input id="id_f" type="url" name="f" maxlength="20" minlength="15" />') self.assertWidgetRendersTo(f, '<input id="id_f" type="url" name="f" maxlength="20" minlength="15" required />')
with self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 15 characters (it has 12).'"): with self.assertRaisesMessage(ValidationError, "'Ensure this value has at least 15 characters (it has 12).'"):
f.clean('http://f.com') f.clean('http://f.com')
self.assertEqual('http://example.com', f.clean('http://example.com')) self.assertEqual('http://example.com', f.clean('http://example.com'))

File diff suppressed because it is too large Load Diff

View File

@ -24,8 +24,8 @@ class FormsRegressionsTestCase(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
TestForm(auto_id=False).as_p(), TestForm(auto_id=False).as_p(),
'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n' '<p>F1: <input type="text" class="special" name="f1" maxlength="10" required /></p>\n'
'<p>F2: <input type="text" class="special" name="f2" /></p>' '<p>F2: <input type="text" class="special" name="f2" required /></p>'
) )
def test_regression_3600(self): def test_regression_3600(self):
@ -39,7 +39,7 @@ class FormsRegressionsTestCase(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
'<p><label for="id_username">username:</label>' '<p><label for="id_username">username:</label>'
'<input id="id_username" type="text" name="username" maxlength="10" /></p>' '<input id="id_username" type="text" name="username" maxlength="10" required /></p>'
) )
# Translations are done at rendering time, so multi-lingual apps can define forms) # Translations are done at rendering time, so multi-lingual apps can define forms)
@ -47,13 +47,13 @@ class FormsRegressionsTestCase(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
'<p><label for="id_username">Benutzername:</label>' '<p><label for="id_username">Benutzername:</label>'
'<input id="id_username" type="text" name="username" maxlength="10" /></p>' '<input id="id_username" type="text" name="username" maxlength="10" required /></p>'
) )
with translation.override('pl'): with translation.override('pl'):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
'<p><label for="id_username">u\u017cytkownik:</label>' '<p><label for="id_username">u\u017cytkownik:</label>'
'<input id="id_username" type="text" name="username" maxlength="10" /></p>' '<input id="id_username" type="text" name="username" maxlength="10" required /></p>'
) )
def test_regression_5216(self): def test_regression_5216(self):
@ -82,12 +82,12 @@ class FormsRegressionsTestCase(TestCase):
'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label>' '<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label>'
'<ul id="id_somechoice">\n' '<ul id="id_somechoice">\n'
'<li><label for="id_somechoice_0">' '<li><label for="id_somechoice_0">'
'<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> ' '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required /> '
'En tied\xe4</label></li>\n' 'En tied\xe4</label></li>\n'
'<li><label for="id_somechoice_1">' '<li><label for="id_somechoice_1">'
'<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> ' '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required /> '
'Mies</label></li>\n<li><label for="id_somechoice_2">' 'Mies</label></li>\n<li><label for="id_somechoice_2">'
'<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> ' '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required /> '
'Nainen</label></li>\n</ul></p>' 'Nainen</label></li>\n</ul></p>'
) )
@ -101,12 +101,12 @@ class FormsRegressionsTestCase(TestCase):
'\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n' '\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n'
'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label>' '<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label>'
' <ul id="id_somechoice">\n<li><label for="id_somechoice_0">' ' <ul id="id_somechoice">\n<li><label for="id_somechoice_0">'
'<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> ' '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required /> '
'En tied\xe4</label></li>\n' 'En tied\xe4</label></li>\n'
'<li><label for="id_somechoice_1">' '<li><label for="id_somechoice_1">'
'<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> ' '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required /> '
'Mies</label></li>\n<li><label for="id_somechoice_2">' 'Mies</label></li>\n<li><label for="id_somechoice_2">'
'<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> ' '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required /> '
'Nainen</label></li>\n</ul></p>' 'Nainen</label></li>\n</ul></p>'
) )

View File

@ -109,24 +109,24 @@ class ModelFormCallableModelDefault(TestCase):
ChoiceOptionModel.objects.create(id=3, name='option 3') ChoiceOptionModel.objects.create(id=3, name='option 3')
self.assertHTMLEqual( self.assertHTMLEqual(
ChoiceFieldForm().as_p(), ChoiceFieldForm().as_p(),
"""<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice"> """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice" required>
<option value="1" selected="selected">ChoiceOption 1</option> <option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option> <option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="1" id="initial-id_choice" /></p> </select><input type="hidden" name="initial-choice" value="1" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int"> <p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int" required>
<option value="1" selected="selected">ChoiceOption 1</option> <option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option> <option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="1" id="initial-id_choice_int" /></p> </select><input type="hidden" name="initial-choice_int" value="1" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <p><label for="id_multi_choice">Multi choice:</label>
<select multiple="multiple" name="multi_choice" id="id_multi_choice"> <select multiple="multiple" name="multi_choice" id="id_multi_choice" required>
<option value="1" selected="selected">ChoiceOption 1</option> <option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option> <option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="1" id="initial-id_multi_choice_0" /></p> </select><input type="hidden" name="initial-multi_choice" value="1" id="initial-id_multi_choice_0" /></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <p><label for="id_multi_choice_int">Multi choice int:</label>
<select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int"> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int" required>
<option value="1" selected="selected">ChoiceOption 1</option> <option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option> <option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
@ -145,25 +145,25 @@ class ModelFormCallableModelDefault(TestCase):
'multi_choice': [obj2, obj3], 'multi_choice': [obj2, obj3],
'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"), 'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"),
}).as_p(), }).as_p(),
"""<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice"> """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice" required>
<option value="1">ChoiceOption 1</option> <option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option> <option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="2" id="initial-id_choice" /></p> </select><input type="hidden" name="initial-choice" value="2" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int"> <p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int" required>
<option value="1">ChoiceOption 1</option> <option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option> <option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option> <option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="2" id="initial-id_choice_int" /></p> </select><input type="hidden" name="initial-choice_int" value="2" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <p><label for="id_multi_choice">Multi choice:</label>
<select multiple="multiple" name="multi_choice" id="id_multi_choice"> <select multiple="multiple" name="multi_choice" id="id_multi_choice" required>
<option value="1">ChoiceOption 1</option> <option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option> <option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option> <option value="3" selected="selected">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="2" id="initial-id_multi_choice_0" /> </select><input type="hidden" name="initial-multi_choice" value="2" id="initial-id_multi_choice_0" />
<input type="hidden" name="initial-multi_choice" value="3" id="initial-id_multi_choice_1" /></p> <input type="hidden" name="initial-multi_choice" value="3" id="initial-id_multi_choice_1" /></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <p><label for="id_multi_choice_int">Multi choice int:</label>
<select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int"> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int" required>
<option value="1">ChoiceOption 1</option> <option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option> <option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option> <option value="3" selected="selected">ChoiceOption 3</option>
@ -308,7 +308,7 @@ class EmptyLabelTestCase(TestCase):
f = EmptyCharLabelChoiceForm() f = EmptyCharLabelChoiceForm()
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" required /></p>
<p><label for="id_choice">Choice:</label> <select id="id_choice" name="choice"> <p><label for="id_choice">Choice:</label> <select id="id_choice" name="choice">
<option value="" selected="selected">No Preference</option> <option value="" selected="selected">No Preference</option>
<option value="f">Foo</option> <option value="f">Foo</option>
@ -320,7 +320,7 @@ class EmptyLabelTestCase(TestCase):
f = EmptyCharLabelNoneChoiceForm() f = EmptyCharLabelNoneChoiceForm()
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" required /></p>
<p><label for="id_choice_string_w_none">Choice string w none:</label> <p><label for="id_choice_string_w_none">Choice string w none:</label>
<select id="id_choice_string_w_none" name="choice_string_w_none"> <select id="id_choice_string_w_none" name="choice_string_w_none">
<option value="" selected="selected">No Preference</option> <option value="" selected="selected">No Preference</option>
@ -350,7 +350,7 @@ class EmptyLabelTestCase(TestCase):
f = EmptyIntegerLabelChoiceForm() f = EmptyIntegerLabelChoiceForm()
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" required /></p>
<p><label for="id_choice_integer">Choice integer:</label> <p><label for="id_choice_integer">Choice integer:</label>
<select id="id_choice_integer" name="choice_integer"> <select id="id_choice_integer" name="choice_integer">
<option value="" selected="selected">No Preference</option> <option value="" selected="selected">No Preference</option>
@ -370,7 +370,7 @@ class EmptyLabelTestCase(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> """<p><label for="id_name">Name:</label>
<input id="id_name" maxlength="10" name="name" type="text" value="none-test"/></p> <input id="id_name" maxlength="10" name="name" type="text" value="none-test" required /></p>
<p><label for="id_choice_integer">Choice integer:</label> <p><label for="id_choice_integer">Choice integer:</label>
<select id="id_choice_integer" name="choice_integer"> <select id="id_choice_integer" name="choice_integer">
<option value="" selected="selected">No Preference</option> <option value="" selected="selected">No Preference</option>
@ -384,7 +384,7 @@ class EmptyLabelTestCase(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> """<p><label for="id_name">Name:</label>
<input id="id_name" maxlength="10" name="name" type="text" value="foo-test"/></p> <input id="id_name" maxlength="10" name="name" type="text" value="foo-test" required /></p>
<p><label for="id_choice_integer">Choice integer:</label> <p><label for="id_choice_integer">Choice integer:</label>
<select id="id_choice_integer" name="choice_integer"> <select id="id_choice_integer" name="choice_integer">
<option value="">No Preference</option> <option value="">No Preference</option>

View File

@ -1108,20 +1108,22 @@ class FormattingTests(SimpleTestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
form6.as_ul(), form6.as_ul(),
'<li><label for="id_name">Name:</label>' '<li><label for="id_name">Name:</label>'
'<input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>' '<input id="id_name" type="text" name="name" value="acme" maxlength="50" required /></li>'
'<li><label for="id_date_added">Date added:</label>' '<li><label for="id_date_added">Date added:</label>'
'<input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>' '<input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" required /></li>'
'<li><label for="id_cents_paid">Cents paid:</label>' '<li><label for="id_cents_paid">Cents paid:</label>'
'<input type="text" name="cents_paid" value="59,47" id="id_cents_paid" /></li>' '<input type="text" name="cents_paid" value="59,47" id="id_cents_paid" required /></li>'
'<li><label for="id_products_delivered">Products delivered:</label>' '<li><label for="id_products_delivered">Products delivered:</label>'
'<input type="text" name="products_delivered" value="12000" id="id_products_delivered" /></li>' '<input type="text" name="products_delivered" value="12000" id="id_products_delivered" required />'
'</li>'
) )
self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00') self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00')
self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added']) self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
with self.settings(USE_THOUSAND_SEPARATOR=True): with self.settings(USE_THOUSAND_SEPARATOR=True):
# Checking for the localized "products_delivered" field # Checking for the localized "products_delivered" field
self.assertInHTML( self.assertInHTML(
'<input type="text" name="products_delivered" value="12.000" id="id_products_delivered" />', '<input type="text" name="products_delivered" '
'value="12.000" id="id_products_delivered" required />',
form6.as_ul() form6.as_ul()
) )
@ -1247,13 +1249,13 @@ class FormattingTests(SimpleTestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
template.render(context), template.render(context),
'<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" />;' '<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" required />;'
'<input id="id_cents_paid" name="cents_paid" type="text" value="59,47" />' '<input id="id_cents_paid" name="cents_paid" type="text" value="59,47" required />'
) )
self.assertHTMLEqual( self.assertHTMLEqual(
template_as_text.render(context), template_as_text.render(context),
'<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" />;' '<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" required />;'
' <input id="id_cents_paid" name="cents_paid" type="text" value="59,47" />' ' <input id="id_cents_paid" name="cents_paid" type="text" value="59,47" required />'
) )
self.assertHTMLEqual( self.assertHTMLEqual(
template_as_hidden.render(context), template_as_hidden.render(context),

View File

@ -525,11 +525,11 @@ class ModelFormBaseTest(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
str(SubclassMeta()), str(SubclassMeta()),
"""<tr><th><label for="id_name">Name:</label></th> """<tr><th><label for="id_name">Name:</label></th>
<td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr> <td><input id="id_name" type="text" name="name" maxlength="20" required /></td></tr>
<tr><th><label for="id_slug">Slug:</label></th> <tr><th><label for="id_slug">Slug:</label></th>
<td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr> <td><input id="id_slug" type="text" name="slug" maxlength="20" required /></td></tr>
<tr><th><label for="id_checkbox">Checkbox:</label></th> <tr><th><label for="id_checkbox">Checkbox:</label></th>
<td><input type="checkbox" name="checkbox" id="id_checkbox" /></td></tr>""" <td><input type="checkbox" name="checkbox" id="id_checkbox" required /></td></tr>"""
) )
def test_orderfields_form(self): def test_orderfields_form(self):
@ -543,9 +543,9 @@ class ModelFormBaseTest(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
str(OrderFields()), str(OrderFields()),
"""<tr><th><label for="id_url">The URL:</label></th> """<tr><th><label for="id_url">The URL:</label></th>
<td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr> <td><input id="id_url" type="text" name="url" maxlength="40" required /></td></tr>
<tr><th><label for="id_name">Name:</label></th> <tr><th><label for="id_name">Name:</label></th>
<td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>""" <td><input id="id_name" type="text" name="name" maxlength="20" required /></td></tr>"""
) )
def test_orderfields2_form(self): def test_orderfields2_form(self):
@ -591,15 +591,15 @@ class TestFieldOverridesByFormMeta(SimpleTestCase):
form = FieldOverridesByFormMetaForm() form = FieldOverridesByFormMetaForm()
self.assertHTMLEqual( self.assertHTMLEqual(
str(form['name']), str(form['name']),
'<textarea id="id_name" rows="10" cols="40" name="name" maxlength="20"></textarea>', '<textarea id="id_name" rows="10" cols="40" name="name" maxlength="20" required></textarea>',
) )
self.assertHTMLEqual( self.assertHTMLEqual(
str(form['url']), str(form['url']),
'<input id="id_url" type="text" class="url" name="url" maxlength="40" />', '<input id="id_url" type="text" class="url" name="url" maxlength="40" required />',
) )
self.assertHTMLEqual( self.assertHTMLEqual(
str(form['slug']), str(form['slug']),
'<input id="id_slug" type="text" name="slug" maxlength="20" />', '<input id="id_slug" type="text" name="slug" maxlength="20" required />',
) )
def test_label_overrides(self): def test_label_overrides(self):
@ -1041,29 +1041,29 @@ class ModelFormBasicTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
str(f), str(f),
"""<tr><th><label for="id_name">Name:</label></th> """<tr><th><label for="id_name">Name:</label></th>
<td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr> <td><input id="id_name" type="text" name="name" maxlength="20" required /></td></tr>
<tr><th><label for="id_slug">Slug:</label></th> <tr><th><label for="id_slug">Slug:</label></th>
<td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr> <td><input id="id_slug" type="text" name="slug" maxlength="20" required /></td></tr>
<tr><th><label for="id_url">The URL:</label></th> <tr><th><label for="id_url">The URL:</label></th>
<td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>""" <td><input id="id_url" type="text" name="url" maxlength="40" required /></td></tr>"""
) )
self.assertHTMLEqual( self.assertHTMLEqual(
str(f.as_ul()), str(f.as_ul()),
"""<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" /></li> """<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" required /></li>
<li><label for="id_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" /></li> <li><label for="id_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" required /></li>
<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></li>""" <li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" required /></li>"""
) )
self.assertHTMLEqual( self.assertHTMLEqual(
str(f["name"]), str(f["name"]),
"""<input id="id_name" type="text" name="name" maxlength="20" />""") """<input id="id_name" type="text" name="name" maxlength="20" required />""")
def test_auto_id(self): def test_auto_id(self):
f = BaseCategoryForm(auto_id=False) f = BaseCategoryForm(auto_id=False)
self.assertHTMLEqual( self.assertHTMLEqual(
str(f.as_ul()), str(f.as_ul()),
"""<li>Name: <input type="text" name="name" maxlength="20" /></li> """<li>Name: <input type="text" name="name" maxlength="20" required /></li>
<li>Slug: <input type="text" name="slug" maxlength="20" /></li> <li>Slug: <input type="text" name="slug" maxlength="20" required /></li>
<li>The URL: <input type="text" name="url" maxlength="40" /></li>""" <li>The URL: <input type="text" name="url" maxlength="40" required /></li>"""
) )
def test_initial_values(self): def test_initial_values(self):
@ -1077,15 +1077,15 @@ class ModelFormBasicTests(TestCase):
}) })
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" /></li> <li>Pub date: <input type="text" name="pub_date" required /></li>
<li>Writer: <select name="writer"> <li>Writer: <select name="writer" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></li> </select></li>
<li>Article: <textarea rows="10" cols="40" name="article"></textarea></li> <li>Article: <textarea rows="10" cols="40" name="article" required></textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="%s" selected="selected">Entertainment</option> <option value="%s" selected="selected">Entertainment</option>
<option value="%s" selected="selected">It&#39;s a test</option> <option value="%s" selected="selected">It&#39;s a test</option>
@ -1103,7 +1103,7 @@ class ModelFormBasicTests(TestCase):
f = RoykoForm(auto_id=False, instance=self.w_royko) f = RoykoForm(auto_id=False, instance=self.w_royko)
self.assertHTMLEqual( self.assertHTMLEqual(
six.text_type(f), six.text_type(f),
'''<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br /> '''<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" required /><br />
<span class="helptext">Use both first and last names.</span></td></tr>''' <span class="helptext">Use both first and last names.</span></td></tr>'''
) )
@ -1119,15 +1119,15 @@ class ModelFormBasicTests(TestCase):
f = ArticleForm(auto_id=False, instance=art) f = ArticleForm(auto_id=False, instance=art)
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li> <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required /></li>
<li>Writer: <select name="writer"> <li>Writer: <select name="writer" required>
<option value="">---------</option> <option value="">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s" selected="selected">Mike Royko</option> <option value="%s" selected="selected">Mike Royko</option>
</select></li> </select></li>
<li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li> <li>Article: <textarea rows="10" cols="40" name="article" required>Hello.</textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="%s">Entertainment</option> <option value="%s">Entertainment</option>
<option value="%s">It&#39;s a test</option> <option value="%s">It&#39;s a test</option>
@ -1174,7 +1174,7 @@ class ModelFormBasicTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
form.as_ul(), form.as_ul(),
"""<li><label for="id_headline">Headline:</label> """<li><label for="id_headline">Headline:</label>
<input id="id_headline" type="text" name="headline" maxlength="50" /></li> <input id="id_headline" type="text" name="headline" maxlength="50" required /></li>
<li><label for="id_categories">Categories:</label> <li><label for="id_categories">Categories:</label>
<select multiple="multiple" name="categories" id="id_categories"> <select multiple="multiple" name="categories" id="id_categories">
<option value="%d" selected="selected">Entertainment</option> <option value="%d" selected="selected">Entertainment</option>
@ -1235,15 +1235,15 @@ class ModelFormBasicTests(TestCase):
f = ArticleForm(auto_id=False) f = ArticleForm(auto_id=False)
self.assertHTMLEqual( self.assertHTMLEqual(
six.text_type(f), six.text_type(f),
'''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> '''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required /></td></tr>
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr> <tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" required /></td></tr>
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> <tr><th>Pub date:</th><td><input type="text" name="pub_date" required /></td></tr>
<tr><th>Writer:</th><td><select name="writer"> <tr><th>Writer:</th><td><select name="writer" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></td></tr> </select></td></tr>
<tr><th>Article:</th><td><textarea rows="10" cols="40" name="article"></textarea></td></tr> <tr><th>Article:</th><td><textarea rows="10" cols="40" name="article" required></textarea></td></tr>
<tr><th>Categories:</th><td><select multiple="multiple" name="categories"> <tr><th>Categories:</th><td><select multiple="multiple" name="categories">
<option value="%s">Entertainment</option> <option value="%s">Entertainment</option>
<option value="%s">It&#39;s a test</option> <option value="%s">It&#39;s a test</option>
@ -1265,15 +1265,15 @@ class ModelFormBasicTests(TestCase):
f = ArticleForm(auto_id=False, instance=new_art) f = ArticleForm(auto_id=False, instance=new_art)
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li> <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required /></li>
<li>Writer: <select name="writer"> <li>Writer: <select name="writer" required>
<option value="">---------</option> <option value="">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s" selected="selected">Mike Royko</option> <option value="%s" selected="selected">Mike Royko</option>
</select></li> </select></li>
<li>Article: <textarea rows="10" cols="40" name="article">Hello.</textarea></li> <li>Article: <textarea rows="10" cols="40" name="article" required>Hello.</textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="%s" selected="selected">Entertainment</option> <option value="%s" selected="selected">Entertainment</option>
<option value="%s">It&#39;s a test</option> <option value="%s">It&#39;s a test</option>
@ -1301,8 +1301,8 @@ class ModelFormBasicTests(TestCase):
f = PartialArticleForm(auto_id=False) f = PartialArticleForm(auto_id=False)
self.assertHTMLEqual( self.assertHTMLEqual(
six.text_type(f), six.text_type(f),
'''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> '''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required /></td></tr>
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>''') <tr><th>Pub date:</th><td><input type="text" name="pub_date" required /></td></tr>''')
# You can create a form over a subset of the available fields # You can create a form over a subset of the available fields
# by specifying a 'fields' argument to form_for_instance. # by specifying a 'fields' argument to form_for_instance.
@ -1322,9 +1322,9 @@ class ModelFormBasicTests(TestCase):
}, auto_id=False, instance=art) }, auto_id=False, instance=art)
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>''' <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required /></li>'''
) )
self.assertTrue(f.is_valid()) self.assertTrue(f.is_valid())
new_art = f.save() new_art = f.save()
@ -1411,15 +1411,15 @@ class ModelFormBasicTests(TestCase):
f = ArticleForm(auto_id=False) f = ArticleForm(auto_id=False)
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" /></li> <li>Pub date: <input type="text" name="pub_date" required /></li>
<li>Writer: <select name="writer"> <li>Writer: <select name="writer" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></li> </select></li>
<li>Article: <textarea rows="10" cols="40" name="article"></textarea></li> <li>Article: <textarea rows="10" cols="40" name="article" required></textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="%s">Entertainment</option> <option value="%s">Entertainment</option>
<option value="%s">It&#39;s a test</option> <option value="%s">It&#39;s a test</option>
@ -1436,16 +1436,16 @@ class ModelFormBasicTests(TestCase):
w_bernstein = Writer.objects.create(name='Carl Bernstein') w_bernstein = Writer.objects.create(name='Carl Bernstein')
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
'''<li>Headline: <input type="text" name="headline" maxlength="50" /></li> '''<li>Headline: <input type="text" name="headline" maxlength="50" required /></li>
<li>Slug: <input type="text" name="slug" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
<li>Pub date: <input type="text" name="pub_date" /></li> <li>Pub date: <input type="text" name="pub_date" required /></li>
<li>Writer: <select name="writer"> <li>Writer: <select name="writer" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s">Carl Bernstein</option> <option value="%s">Carl Bernstein</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></li> </select></li>
<li>Article: <textarea rows="10" cols="40" name="article"></textarea></li> <li>Article: <textarea rows="10" cols="40" name="article" required></textarea></li>
<li>Categories: <select multiple="multiple" name="categories"> <li>Categories: <select multiple="multiple" name="categories">
<option value="%s">Entertainment</option> <option value="%s">Entertainment</option>
<option value="%s">It&#39;s a test</option> <option value="%s">It&#39;s a test</option>
@ -1798,12 +1798,12 @@ class ModelOneToOneFieldTests(TestCase):
form = WriterProfileForm() form = WriterProfileForm()
self.assertHTMLEqual( self.assertHTMLEqual(
form.as_p(), form.as_p(),
'''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer"> '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required>
<option value="" selected="selected">---------</option> <option value="" selected="selected">---------</option>
<option value="%s">Bob Woodward</option> <option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></p> </select></p>
<p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" min="0" /></p>''' % ( <p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" min="0" required /></p>''' % (
self.w_woodward.pk, self.w_royko.pk, self.w_woodward.pk, self.w_royko.pk,
) )
) )
@ -1819,12 +1819,13 @@ class ModelOneToOneFieldTests(TestCase):
form = WriterProfileForm(instance=instance) form = WriterProfileForm(instance=instance)
self.assertHTMLEqual( self.assertHTMLEqual(
form.as_p(), form.as_p(),
'''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer"> '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required>
<option value="">---------</option> <option value="">---------</option>
<option value="%s" selected="selected">Bob Woodward</option> <option value="%s" selected="selected">Bob Woodward</option>
<option value="%s">Mike Royko</option> <option value="%s">Mike Royko</option>
</select></p> </select></p>
<p><label for="id_age">Age:</label> <input type="number" name="age" value="65" id="id_age" min="0" /></p>''' % ( <p><label for="id_age">Age:</label>
<input type="number" name="age" value="65" id="id_age" min="0" required /></p>''' % (
self.w_woodward.pk, self.w_royko.pk, self.w_woodward.pk, self.w_royko.pk,
) )
) )
@ -2403,9 +2404,9 @@ class OtherModelFormTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
six.text_type(CategoryForm()), six.text_type(CategoryForm()),
'''<tr><th><label for="id_description">Description:</label></th> '''<tr><th><label for="id_description">Description:</label></th>
<td><input type="text" name="description" id="id_description" /></td></tr> <td><input type="text" name="description" id="id_description" required /></td></tr>
<tr><th><label for="id_url">The URL:</label></th> <tr><th><label for="id_url">The URL:</label></th>
<td><input id="id_url" type="text" name="url" maxlength="40" /></td></tr>''' <td><input id="id_url" type="text" name="url" maxlength="40" required /></td></tr>'''
) )
# to_field_name should also work on ModelMultipleChoiceField ################## # to_field_name should also work on ModelMultipleChoiceField ##################
@ -2424,7 +2425,7 @@ class OtherModelFormTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
six.text_type(CustomFieldForExclusionForm()), six.text_type(CustomFieldForExclusionForm()),
'''<tr><th><label for="id_name">Name:</label></th> '''<tr><th><label for="id_name">Name:</label></th>
<td><input id="id_name" type="text" name="name" maxlength="10" /></td></tr>''' <td><input id="id_name" type="text" name="name" maxlength="10" required /></td></tr>'''
) )
def test_iterable_model_m2m(self): def test_iterable_model_m2m(self):
@ -2438,8 +2439,9 @@ class OtherModelFormTests(TestCase):
self.maxDiff = 1024 self.maxDiff = 1024
self.assertHTMLEqual( self.assertHTMLEqual(
form.as_p(), form.as_p(),
"""<p><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="50" /></p> """<p><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="50" required /></p>
<p><label for="id_colours">Colours:</label> <select multiple="multiple" name="colours" id="id_colours"> <p><label for="id_colours">Colours:</label>
<select multiple="multiple" name="colours" id="id_colours" required>
<option value="%(blue_pk)s">Blue</option> <option value="%(blue_pk)s">Blue</option>
</select></p>""" </select></p>"""
% {'blue_pk': colour.pk}) % {'blue_pk': colour.pk})
@ -2456,15 +2458,16 @@ class OtherModelFormTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
form.as_p(), form.as_p(),
""" """
<p><label for="id_title">Title:</label> <input id="id_title" maxlength="30" name="title" type="text" /></p> <p><label for="id_title">Title:</label>
<input id="id_title" maxlength="30" name="title" type="text" required /></p>
<p><label for="id_date_published">Date published:</label> <p><label for="id_date_published">Date published:</label>
<input id="id_date_published" name="date_published" type="text" value="{0}" /> <input id="id_date_published" name="date_published" type="text" value="{0}" required />
<input id="initial-id_date_published" name="initial-date_published" type="hidden" value="{0}" /></p> <input id="initial-id_date_published" name="initial-date_published" type="hidden" value="{0}" /></p>
<p><label for="id_mode">Mode:</label> <select id="id_mode" name="mode"> <p><label for="id_mode">Mode:</label> <select id="id_mode" name="mode" required>
<option value="di" selected="selected">direct</option> <option value="di" selected="selected">direct</option>
<option value="de">delayed</option></select> <option value="de">delayed</option></select>
<input id="initial-id_mode" name="initial-mode" type="hidden" value="di" /></p> <input id="initial-id_mode" name="initial-mode" type="hidden" value="di" /></p>
<p><label for="id_category">Category:</label> <select id="id_category" name="category"> <p><label for="id_category">Category:</label> <select id="id_category" name="category" required>
<option value="1">Games</option> <option value="1">Games</option>
<option value="2">Comics</option> <option value="2">Comics</option>
<option value="3" selected="selected">Novel</option></select> <option value="3" selected="selected">Novel</option></select>

View File

@ -1641,7 +1641,7 @@ class TestModelFormsetOverridesTroughFormMeta(TestCase):
form = PoetFormSet.form() form = PoetFormSet.form()
self.assertHTMLEqual( self.assertHTMLEqual(
"%s" % form['name'], "%s" % form['name'],
'<input id="id_name" maxlength="100" type="text" class="poet" name="name" />' '<input id="id_name" maxlength="100" type="text" class="poet" name="name" required />'
) )
def test_inlineformset_factory_widgets(self): def test_inlineformset_factory_widgets(self):
@ -1652,7 +1652,7 @@ class TestModelFormsetOverridesTroughFormMeta(TestCase):
form = BookFormSet.form() form = BookFormSet.form()
self.assertHTMLEqual( self.assertHTMLEqual(
"%s" % form['title'], "%s" % form['title'],
'<input class="book" id="id_title" maxlength="100" name="title" type="text" />' '<input class="book" id="id_title" maxlength="100" name="title" type="text" required />'
) )
def test_modelformset_factory_labels_overrides(self): def test_modelformset_factory_labels_overrides(self):

View File

@ -359,7 +359,7 @@ class ModelAdminTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
str(form["main_band"]), str(form["main_band"]),
'<div class="related-widget-wrapper">' '<div class="related-widget-wrapper">'
'<select name="main_band" id="id_main_band">' '<select name="main_band" id="id_main_band" required>'
'<option value="" selected="selected">---------</option>' '<option value="" selected="selected">---------</option>'
'<option value="%d">The Beatles</option>' '<option value="%d">The Beatles</option>'
'<option value="%d">The Doors</option>' '<option value="%d">The Doors</option>'
@ -380,7 +380,7 @@ class ModelAdminTests(TestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
str(form["main_band"]), str(form["main_band"]),
'<div class="related-widget-wrapper">' '<div class="related-widget-wrapper">'
'<select name="main_band" id="id_main_band">' '<select name="main_band" id="id_main_band" required>'
'<option value="" selected="selected">---------</option>' '<option value="" selected="selected">---------</option>'
'<option value="%d">The Doors</option>' '<option value="%d">The Doors</option>'
'</select></div>' % self.band.id '</select></div>' % self.band.id

View File

@ -699,9 +699,9 @@ class TestSplitFormField(PostgreSQLTestCase):
<tr> <tr>
<th><label for="id_array_0">Array:</label></th> <th><label for="id_array_0">Array:</label></th>
<td> <td>
<input id="id_array_0" name="array_0" type="text" /> <input id="id_array_0" name="array_0" type="text" required />
<input id="id_array_1" name="array_1" type="text" /> <input id="id_array_1" name="array_1" type="text" required />
<input id="id_array_2" name="array_2" type="text" /> <input id="id_array_2" name="array_2" type="text" required />
</td> </td>
</tr> </tr>
''') ''')