Browse Source

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

Jon Dufresne 9 years ago
parent
commit
ec6121693f

+ 2 - 0
django/forms/boundfield.py

@@ -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

+ 5 - 1
django/forms/forms.py

@@ -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.

+ 5 - 0
django/forms/formsets.py

@@ -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)

+ 5 - 3
django/forms/models.py

@@ -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,
-                                            error_class, label_suffix, empty_permitted)
+        super(BaseModelForm, self).__init__(
+            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]

+ 100 - 89
docs/ref/forms/api.txt

@@ -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>Url:</th><td><input type="url" name="url" /></td></tr>
-    <tr><th>Comment:</th><td><input type="text" name="comment" /></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" required /></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_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></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_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" required /></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_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></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_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" 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" 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_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
-    <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></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" required /></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_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
-    <li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></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" required /></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_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></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_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" required /></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>Message:</th><td><input type="text" name="message" /></td></tr>
-    <tr><th>Sender:</th><td><input type="email" name="sender" /></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" required /></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>Message: <input type="text" name="message" /></li>
-    <li>Sender: <input type="email" name="sender" /></li>
+    <li>Subject: <input type="text" name="subject" maxlength="100" required /></li>
+    <li>Message: <input type="text" name="message" required /></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>Message: <input type="text" name="message" /></p>
-    <p>Sender: <input type="email" name="sender" /></p>
+    <p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
+    <p>Message: <input type="text" name="message" required /></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="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
-    <tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" /></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" required /></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="message">Message:</label> <input type="text" name="message" id="message" /></li>
-    <li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></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" required /></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="message">Message:</label> <input type="text" name="message" id="message" /></p>
-    <p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></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" required /></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_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></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_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" required /></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_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
-    <li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></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" required /></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_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
-    <p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></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" required /></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_message">Message</label> <input type="text" name="message" id="id_for_message" /></li>
-    <li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" /></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" required /></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_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li>
-    <li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" /></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" required /></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>Message:</th><td><input type="text" name="message" value="Hi there" /></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>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" 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" 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>Message: <input type="text" name="message" value="Hi there" /></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>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" required /></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>Message: <input type="text" name="message" value="Hi there" /></p>
+    <p>Subject: <input type="text" name="subject" maxlength="100" required /></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>Message: <input type="text" name="message" value="Hi there" /></p>
+    <p>Subject: <input type="text" name="subject" maxlength="100" required /></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 type="text" name="message" id="id_message" />
-    <input type="email" name="sender" id="id_sender" />
+    <input id="id_subject" type="text" name="subject" maxlength="100" required />
+    <input type="text" name="message" id="id_message" required />
+    <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>Message: <input type="text" name="message" /></li>
-    <li>Sender: <input type="email" name="sender" /></li>
+    <li>Subject: <input type="text" name="subject" maxlength="100" required /></li>
+    <li>Message: <input type="text" name="message" required /></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>Last name: <input type="text" name="last_name" /></li>
-    <li>Instrument: <input type="text" name="instrument" /></li>
-    <li>Haircut type: <input type="text" name="haircut_type" /></li>
+    <li>First name: <input type="text" name="first_name" required /></li>
+    <li>Last name: <input type="text" name="last_name" required /></li>
+    <li>Instrument: <input type="text" name="instrument" required /></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-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_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" 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-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_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" required /></li>
 
 
 The prefix can also be specified on the form class::
 The prefix can also be specified on the form class::
 
 

+ 22 - 22
docs/ref/forms/fields.txt

@@ -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 website:</th><td><input type="url" name="url" /></td></tr>
-    <tr><th>Comment:</th><td><input type="text" name="comment" /></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" required /></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_nationality">Nationality?</label> <input id="id_nationality" name="nationality" type="text" /></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_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" required /></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>Url:</th><td><input type="url" name="url" value="http://" /></td></tr>
-    <tr><th>Comment:</th><td><input type="text" name="comment" /></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://" required /></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>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="url" name="url" value="http://" /></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>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://" required /></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>Message:</th><td><input type="text" name="message" /></td></tr>
-    <tr><th>Sender:</th><td><input type="email" name="sender" /><br />A valid email address, please.</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" required /></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>Message: <input type="text" name="message" /></li>
-    <li>Sender: <input type="email" name="sender" /> A valid email address, please.</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" required /></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>Message: <input type="text" name="message" /></p>
-    <p>Sender: <input type="email" name="sender" /> A valid email address, please.</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" required /></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``

+ 15 - 15
docs/ref/forms/widgets.txt

@@ -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>Url:</th><td><input type="url" name="url"/></td></tr>
-    <tr><th>Comment:</th><td><input type="text" name="comment" /></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" required /></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>Url:</th><td><input type="url" name="url"/></td></tr>
-    <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></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" required /></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

+ 11 - 0
docs/releases/1.10.txt

@@ -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

+ 5 - 0
docs/topics/forms/formsets.txt

@@ -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

+ 4 - 4
docs/topics/forms/index.txt

@@ -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>
 
 

+ 1 - 1
tests/admin_views/tests.py

@@ -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>

+ 1 - 1
tests/forms_tests/field_tests/test_charfield.py

@@ -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 />')

+ 1 - 1
tests/forms_tests/field_tests/test_choicefield.py

@@ -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>'
         )
         )

+ 7 - 4
tests/forms_tests/field_tests/test_decimalfield.py

@@ -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)

+ 1 - 1
tests/forms_tests/field_tests/test_durationfield.py

@@ -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):

+ 5 - 2
tests/forms_tests/field_tests/test_emailfield.py

@@ -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'))

+ 10 - 4
tests/forms_tests/field_tests/test_floatfield.py

@@ -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()

+ 5 - 5
tests/forms_tests/field_tests/test_integerfield.py

@@ -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()

+ 8 - 8
tests/forms_tests/field_tests/test_multivaluefield.py

@@ -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" />
-            <select multiple="multiple" name="field1_1" id="id_field1_1">
+            <td><input type="text" name="field1_0" id="id_field1_0" required />
+            <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_1" id="id_field1_2_1" /></td></tr>
+            <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" 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" />
-            <select multiple="multiple" name="field1_1" id="id_field1_1">
+            <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" 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_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
+            <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" required /></td></tr>
             """,
             """,
         )
         )
 
 

+ 2 - 2
tests/forms_tests/field_tests/test_urlfield.py

@@ -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
+ 249 - 222
tests/forms_tests/tests/test_forms.py


+ 11 - 11
tests/forms_tests/tests/test_regressions.py

@@ -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>F2: <input type="text" class="special" name="f2" /></p>'
+            '<p>F1: <input type="text" class="special" name="f1" maxlength="10" required /></p>\n'
+            '<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>'
             )
             )
 
 

+ 13 - 13
tests/forms_tests/tests/tests.py

@@ -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>

+ 11 - 9
tests/i18n/tests.py

@@ -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_cents_paid" name="cents_paid" type="text" value="59,47" />'
+                '<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" 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_cents_paid" name="cents_paid" type="text" value="59,47" />'
+                '<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" required />'
             )
             )
             self.assertHTMLEqual(
             self.assertHTMLEqual(
                 template_as_hidden.render(context),
                 template_as_hidden.render(context),

+ 71 - 68
tests/model_forms/tests.py

@@ -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_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" /></li>
-<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" /></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" required /></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>Slug: <input type="text" name="slug" maxlength="20" /></li>
-<li>The URL: <input type="text" name="url" maxlength="40" /></li>"""
+            """<li>Name: <input type="text" name="name" maxlength="20" required /></li>
+<li>Slug: <input type="text" name="slug" maxlength="20" required /></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>Slug: <input type="text" name="slug" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" /></li>
-<li>Writer: <select name="writer">
+            '''<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" required /></li>
+<li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
+<li>Pub date: <input type="text" name="pub_date" required /></li>
+<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>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
-<li>Writer: <select name="writer">
+            '''<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" required /></li>
+<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required /></li>
+<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>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
-<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
-<tr><th>Writer:</th><td><select name="writer">
+            '''<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" required /></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" 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>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li>
-<li>Writer: <select name="writer">
+            '''<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" required /></li>
+<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required /></li>
+<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>Pub date:</th><td><input type="text" name="pub_date" /></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" 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>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></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" required /></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>Slug: <input type="text" name="slug" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" /></li>
-<li>Writer: <select name="writer">
+            '''<li>Headline: <input type="text" name="headline" maxlength="50" required /></li>
+<li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
+<li>Pub date: <input type="text" name="pub_date" required /></li>
+<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>Slug: <input type="text" name="slug" maxlength="50" /></li>
-<li>Pub date: <input type="text" name="pub_date" /></li>
-<li>Writer: <select name="writer">
+            '''<li>Headline: <input type="text" name="headline" maxlength="50" required /></li>
+<li>Slug: <input type="text" name="slug" maxlength="50" required /></li>
+<li>Pub date: <input type="text" name="pub_date" required /></li>
+<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_colours">Colours:</label> <select multiple="multiple" name="colours" id="id_colours">
+            """<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" 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>

+ 2 - 2
tests/model_formsets/tests.py

@@ -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):

+ 2 - 2
tests/modeladmin/tests.py

@@ -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

+ 3 - 3
tests/postgres_tests/test_array.py

@@ -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_1" name="array_1" type="text" />
-                    <input id="id_array_2" name="array_2" type="text" />
+                    <input id="id_array_0" name="array_0" type="text" required />
+                    <input id="id_array_1" name="array_1" type="text" required />
+                    <input id="id_array_2" name="array_2" type="text" required />
                 </td>
                 </td>
             </tr>
             </tr>
         ''')
         ''')

Some files were not shown because too many files changed in this diff