Browse Source

Refs #32338 -- Made RadioSelect/CheckboxSelectMultiple render in <div> tags.

This improves accessibility for screen reader users.
David Smith 3 years ago
parent
commit
5942ab5eb1

+ 5 - 5
django/forms/jinja2/django/forms/widgets/multiple_input.html

@@ -1,5 +1,5 @@
-{% set id = widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
-  <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for widget in options %}
-    <li>{% include widget.template_name %}</li>{% endfor %}{% if group %}
-  </ul></li>{% endif %}{% endfor %}
-</ul>
+{% set id = widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
+  <div><label>{{ group }}</label>{% endif %}{% for widget in options %}<div>
+    {% include widget.template_name %}</div>{% endfor %}{% if group %}
+  </div>{% endif %}{% endfor %}
+</div>

+ 5 - 5
django/forms/templates/django/forms/widgets/multiple_input.html

@@ -1,5 +1,5 @@
-{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
-  <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
-    <li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %}
-  </ul></li>{% endif %}{% endfor %}
-</ul>{% endwith %}
+{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
+  <div><label>{{ group }}</label>{% endif %}{% for option in options %}<div>
+    {% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
+  </div>{% endif %}{% endfor %}
+</div>{% endwith %}

+ 21 - 11
docs/ref/forms/widgets.txt

@@ -703,14 +703,19 @@ that specifies the template used to render each choice. For example, for the
     * ``option_template_name``: ``'django/forms/widgets/radio_option.html'``
     * ``option_template_name``: ``'django/forms/widgets/radio_option.html'``
 
 
     Similar to :class:`Select`, but rendered as a list of radio buttons within
     Similar to :class:`Select`, but rendered as a list of radio buttons within
-    ``<li>`` tags:
+    ``<div>`` tags:
 
 
     .. code-block:: html
     .. code-block:: html
 
 
-        <ul>
-          <li><input type="radio" name="..."></li>
+        <div>
+          <div><input type="radio" name="..."></div>
           ...
           ...
-        </ul>
+        </div>
+
+    .. versionchanged:: 4.0
+
+        So they are announced more concisely by screen readers, radio buttons
+        were changed to render in ``<div>`` tags.
 
 
     For more granular control over the generated markup, you can loop over the
     For more granular control over the generated markup, you can loop over the
     radio buttons in the template. Assuming a form ``myform`` with a field
     radio buttons in the template. Assuming a form ``myform`` with a field
@@ -788,10 +793,10 @@ that specifies the template used to render each choice. For example, for the
         </fieldset>
         </fieldset>
 
 
     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
-    includes ``{{ myform.beatles }}`` -- they'll be output in a ``<ul>`` with
-    ``<li>`` tags, as above.
+    includes ``{{ myform.beatles }}`` -- they'll be output in a ``<div>`` with
+    ``<div>`` tags, as above.
 
 
-    The outer ``<ul>`` container receives the ``id`` attribute of the widget,
+    The outer ``<div>`` container receives the ``id`` attribute of the widget,
     if defined, or :attr:`BoundField.auto_id` otherwise.
     if defined, or :attr:`BoundField.auto_id` otherwise.
 
 
     When looping over the radio buttons, the ``label`` and ``input`` tags include
     When looping over the radio buttons, the ``label`` and ``input`` tags include
@@ -810,14 +815,19 @@ that specifies the template used to render each choice. For example, for the
 
 
     .. code-block:: html
     .. code-block:: html
 
 
-        <ul>
-          <li><input type="checkbox" name="..." ></li>
+        <div>
+          <div><input type="checkbox" name="..." ></div>
           ...
           ...
-        </ul>
+        </div>
 
 
-    The outer ``<ul>`` container receives the ``id`` attribute of the widget,
+    The outer ``<div>`` container receives the ``id`` attribute of the widget,
     if defined, or :attr:`BoundField.auto_id` otherwise.
     if defined, or :attr:`BoundField.auto_id` otherwise.
 
 
+    .. versionchanged:: 4.0
+
+        So they are announced more concisely by screen readers, checkboxes were
+        changed to render in ``<div>`` tags.
+
 Like :class:`RadioSelect`, you can loop over the individual checkboxes for the
 Like :class:`RadioSelect`, you can loop over the individual checkboxes for the
 widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the
 widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the
 ``required`` HTML attribute if the field is required because browser validation
 ``required`` HTML attribute if the field is required because browser validation

+ 7 - 0
docs/releases/4.0.txt

@@ -551,6 +551,13 @@ Miscellaneous
   ``django.db.migrations.state.ProjectState.__init__()`` method must now be a
   ``django.db.migrations.state.ProjectState.__init__()`` method must now be a
   set if provided.
   set if provided.
 
 
+* :class:`~django.forms.RadioSelect` and
+  :class:`~django.forms.CheckboxSelectMultiple` widgets are now rendered in
+  ``<div>`` tags so they are announced more concisely by screen readers. If you
+  need the previous behavior, :ref:`override the widget template
+  <overriding-built-in-widget-templates>` with the appropriate template from
+  Django 3.2.
+
 .. _deprecated-features-4.0:
 .. _deprecated-features-4.0:
 
 
 Features deprecated in 4.0
 Features deprecated in 4.0

+ 54 - 54
tests/forms_tests/tests/test_forms.py

@@ -585,20 +585,20 @@ class FormsTestCase(SimpleTestCase):
             language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect)
             language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect)
 
 
         f = FrameworkForm(auto_id=False)
         f = FrameworkForm(auto_id=False)
-        self.assertHTMLEqual(str(f['language']), """<ul>
-<li><label><input type="radio" name="language" value="P" required> Python</label></li>
-<li><label><input type="radio" name="language" value="J" required> Java</label></li>
-</ul>""")
+        self.assertHTMLEqual(str(f['language']), """<div>
+<div><label><input type="radio" name="language" value="P" required> Python</label></div>
+<div><label><input type="radio" name="language" value="J" required> Java</label></div>
+</div>""")
         self.assertHTMLEqual(f.as_table(), """<tr><th>Name:</th><td><input type="text" name="name" required></td></tr>
         self.assertHTMLEqual(f.as_table(), """<tr><th>Name:</th><td><input type="text" name="name" required></td></tr>
-<tr><th>Language:</th><td><ul>
-<li><label><input type="radio" name="language" value="P" required> Python</label></li>
-<li><label><input type="radio" name="language" value="J" required> Java</label></li>
-</ul></td></tr>""")
+<tr><th>Language:</th><td><div>
+<div><label><input type="radio" name="language" value="P" required> Python</label></div>
+<div><label><input type="radio" name="language" value="J" required> Java</label></div>
+</div></td></tr>""")
         self.assertHTMLEqual(f.as_ul(), """<li>Name: <input type="text" name="name" required></li>
         self.assertHTMLEqual(f.as_ul(), """<li>Name: <input type="text" name="name" required></li>
-<li>Language: <ul>
-<li><label><input type="radio" name="language" value="P" required> Python</label></li>
-<li><label><input type="radio" name="language" value="J" required> Java</label></li>
-</ul></li>""")
+<li>Language: <div>
+<div><label><input type="radio" name="language" value="P" required> Python</label></div>
+<div><label><input type="radio" name="language" value="J" required> Java</label></div>
+</div></li>""")
 
 
         # Regarding auto_id and <label>, RadioSelect is a special case. Each radio button
         # Regarding auto_id and <label>, RadioSelect is a special case. Each radio button
         # gets a distinct ID, formed by appending an underscore plus the button's
         # gets a distinct ID, formed by appending an underscore plus the button's
@@ -606,12 +606,12 @@ class FormsTestCase(SimpleTestCase):
         f = FrameworkForm(auto_id='id_%s')
         f = FrameworkForm(auto_id='id_%s')
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             str(f['language']),
             str(f['language']),
-            """<ul id="id_language">
-<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
-Python</label></li>
-<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
-Java</label></li>
-</ul>"""
+            """<div id="id_language">
+<div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
+Python</label></div>
+<div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
+Java</label></div>
+</div>"""
         )
         )
 
 
         # When RadioSelect is used with auto_id, and the whole form is printed
         # When RadioSelect is used with auto_id, and the whole form is printed
@@ -621,32 +621,32 @@ Java</label></li>
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             f.as_table(),
             f.as_table(),
             """<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" required></td></tr>
             """<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" required></td></tr>
-<tr><th><label>Language:</label></th><td><ul id="id_language">
-<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
-Python</label></li>
-<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
-Java</label></li>
-</ul></td></tr>"""
+<tr><th><label>Language:</label></th><td><div id="id_language">
+<div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
+Python</label></div>
+<div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
+Java</label></div>
+</div></td></tr>"""
         )
         )
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             f.as_ul(),
             f.as_ul(),
             """<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></li>
             """<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></li>
-<li><label>Language:</label> <ul id="id_language">
-<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
-Python</label></li>
-<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
-Java</label></li>
-</ul></li>"""
+<li><label>Language:</label> <div id="id_language">
+<div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
+Python</label></div>
+<div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
+Java</label></div>
+</div></li>"""
         )
         )
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             f.as_p(),
             f.as_p(),
             """<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></p>
             """<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></p>
-<p><label>Language:</label> <ul id="id_language">
-<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
-Python</label></li>
-<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
-Java</label></li>
-</ul></p>"""
+<p><label>Language:</label> <div id="id_language">
+<div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
+Python</label></div>
+<div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
+Java</label></div>
+</div></p>"""
         )
         )
 
 
         # Test iterating on individual radios in a template
         # Test iterating on individual radios in a template
@@ -870,20 +870,20 @@ Java</label></li>
             )
             )
 
 
         f = SongForm(auto_id=False)
         f = SongForm(auto_id=False)
-        self.assertHTMLEqual(str(f['composers']), """<ul>
-<li><label><input type="checkbox" name="composers" value="J"> John Lennon</label></li>
-<li><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></li>
-</ul>""")
+        self.assertHTMLEqual(str(f['composers']), """<div>
+<div><label><input type="checkbox" name="composers" value="J"> John Lennon</label></div>
+<div><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
+</div>""")
         f = SongForm({'composers': ['J']}, auto_id=False)
         f = SongForm({'composers': ['J']}, auto_id=False)
-        self.assertHTMLEqual(str(f['composers']), """<ul>
-<li><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></li>
-<li><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></li>
-</ul>""")
+        self.assertHTMLEqual(str(f['composers']), """<div>
+<div><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></div>
+<div><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
+</div>""")
         f = SongForm({'composers': ['J', 'P']}, auto_id=False)
         f = SongForm({'composers': ['J', 'P']}, auto_id=False)
-        self.assertHTMLEqual(str(f['composers']), """<ul>
-<li><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></li>
-<li><label><input checked type="checkbox" name="composers" value="P"> Paul McCartney</label></li>
-</ul>""")
+        self.assertHTMLEqual(str(f['composers']), """<div>
+<div><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></div>
+<div><label><input checked type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
+</div>""")
         # Test iterating on individual checkboxes in a template
         # Test iterating on individual checkboxes in a template
         t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}')
         t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}')
         self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label>
         self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label>
@@ -905,12 +905,12 @@ Java</label></li>
         f = SongForm(auto_id='%s_id')
         f = SongForm(auto_id='%s_id')
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             str(f['composers']),
             str(f['composers']),
-            """<ul id="composers_id">
-<li><label for="composers_id_0">
-<input type="checkbox" name="composers" value="J" id="composers_id_0"> John Lennon</label></li>
-<li><label for="composers_id_1">
-<input type="checkbox" name="composers" value="P" id="composers_id_1"> Paul McCartney</label></li>
-</ul>"""
+            """<div id="composers_id">
+<div><label for="composers_id_0">
+<input type="checkbox" name="composers" value="J" id="composers_id_0"> John Lennon</label></div>
+<div><label for="composers_id_1">
+<input type="checkbox" name="composers" value="P" id="composers_id_1"> Paul McCartney</label></div>
+</div>"""
         )
         )
 
 
     def test_multiple_choice_list_data(self):
     def test_multiple_choice_list_data(self):

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

@@ -57,15 +57,15 @@ class FormsI18nTests(SimpleTestCase):
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             f.as_p(),
             f.as_p(),
             '<p><label>\xc5\xf8\xdf:</label>'
             '<p><label>\xc5\xf8\xdf:</label>'
-            '<ul id="id_somechoice">\n'
-            '<li><label for="id_somechoice_0">'
+            '<div id="id_somechoice">\n'
+            '<div><label for="id_somechoice_0">'
             '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
             '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
-            'En tied\xe4</label></li>\n'
-            '<li><label for="id_somechoice_1">'
+            'En tied\xe4</label></div>\n'
+            '<div><label for="id_somechoice_1">'
             '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
             '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
-            'Mies</label></li>\n<li><label for="id_somechoice_2">'
+            'Mies</label></div>\n<div><label for="id_somechoice_2">'
             '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
             '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
-            'Nainen</label></li>\n</ul></p>'
+            'Nainen</label></div>\n</div></p>'
         )
         )
 
 
         # Translated error messages
         # Translated error messages
@@ -77,14 +77,14 @@ class FormsI18nTests(SimpleTestCase):
                 '\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c'
                 '\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c'
                 '\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n'
                 '\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n'
                 '<p><label>\xc5\xf8\xdf:</label>'
                 '<p><label>\xc5\xf8\xdf:</label>'
-                ' <ul id="id_somechoice">\n<li><label for="id_somechoice_0">'
+                ' <div id="id_somechoice">\n<div><label for="id_somechoice_0">'
                 '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
                 '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
-                'En tied\xe4</label></li>\n'
-                '<li><label for="id_somechoice_1">'
+                'En tied\xe4</label></div>\n'
+                '<div><label for="id_somechoice_1">'
                 '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
                 '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
-                'Mies</label></li>\n<li><label for="id_somechoice_2">'
+                'Mies</label></div>\n<div><label for="id_somechoice_2">'
                 '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
                 '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
-                'Nainen</label></li>\n</ul></p>'
+                'Nainen</label></div>\n</div></p>'
             )
             )
 
 
     def test_select_translated_text(self):
     def test_select_translated_text(self):

+ 77 - 98
tests/forms_tests/widget_tests/test_checkboxselectmultiple.py

@@ -11,39 +11,39 @@ class CheckboxSelectMultipleTest(WidgetTest):
     widget = CheckboxSelectMultiple
     widget = CheckboxSelectMultiple
 
 
     def test_render_value(self):
     def test_render_value(self):
-        self.check_html(self.widget(choices=self.beatles), 'beatles', ['J'], html=(
-            """<ul>
-            <li><label><input checked type="checkbox" name="beatles" value="J"> John</label></li>
-            <li><label><input type="checkbox" name="beatles" value="P"> Paul</label></li>
-            <li><label><input type="checkbox" name="beatles" value="G"> George</label></li>
-            <li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li>
-            </ul>"""
-        ))
+        self.check_html(self.widget(choices=self.beatles), 'beatles', ['J'], html="""
+            <div>
+            <div><label><input checked type="checkbox" name="beatles" value="J"> John</label></div>
+            <div><label><input type="checkbox" name="beatles" value="P"> Paul</label></div>
+            <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
+            <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
+            </div>
+        """)
 
 
     def test_render_value_multiple(self):
     def test_render_value_multiple(self):
-        self.check_html(self.widget(choices=self.beatles), 'beatles', ['J', 'P'], html=(
-            """<ul>
-            <li><label><input checked type="checkbox" name="beatles" value="J"> John</label></li>
-            <li><label><input checked type="checkbox" name="beatles" value="P"> Paul</label></li>
-            <li><label><input type="checkbox" name="beatles" value="G"> George</label></li>
-            <li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li>
-            </ul>"""
-        ))
+        self.check_html(self.widget(choices=self.beatles), 'beatles', ['J', 'P'], html="""
+            <div>
+            <div><label><input checked type="checkbox" name="beatles" value="J"> John</label></div>
+            <div><label><input checked type="checkbox" name="beatles" value="P"> Paul</label></div>
+            <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
+            <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
+            </div>
+        """)
 
 
     def test_render_none(self):
     def test_render_none(self):
         """
         """
         If the value is None, none of the options are selected, even if the
         If the value is None, none of the options are selected, even if the
         choices have an empty option.
         choices have an empty option.
         """
         """
-        self.check_html(self.widget(choices=(('', 'Unknown'),) + self.beatles), 'beatles', None, html=(
-            """<ul>
-            <li><label><input type="checkbox" name="beatles" value=""> Unknown</label></li>
-            <li><label><input type="checkbox" name="beatles" value="J"> John</label></li>
-            <li><label><input type="checkbox" name="beatles" value="P"> Paul</label></li>
-            <li><label><input type="checkbox" name="beatles" value="G"> George</label></li>
-            <li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li>
-            </ul>"""
-        ))
+        self.check_html(self.widget(choices=(('', 'Unknown'),) + self.beatles), 'beatles', None, html="""
+            <div>
+            <div><label><input type="checkbox" name="beatles" value=""> Unknown</label></div>
+            <div><label><input type="checkbox" name="beatles" value="J"> John</label></div>
+            <div><label><input type="checkbox" name="beatles" value="P"> Paul</label></div>
+            <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
+            <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
+            </div>
+        """)
 
 
     def test_nested_choices(self):
     def test_nested_choices(self):
         nested_choices = (
         nested_choices = (
@@ -52,31 +52,23 @@ class CheckboxSelectMultipleTest(WidgetTest):
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
         )
         )
         html = """
         html = """
-        <ul id="media">
-        <li>
-        <label for="media_0"><input id="media_0" name="nestchoice" type="checkbox" value="unknown"> Unknown</label>
-        </li>
-        <li>Audio<ul id="media_1">
-        <li>
-        <label for="media_1_0">
-        <input checked id="media_1_0" name="nestchoice" type="checkbox" value="vinyl"> Vinyl
-        </label>
-        </li>
-        <li>
-        <label for="media_1_1"><input id="media_1_1" name="nestchoice" type="checkbox" value="cd"> CD</label>
-        </li>
-        </ul></li>
-        <li>Video<ul id="media_2">
-        <li>
-        <label for="media_2_0"><input id="media_2_0" name="nestchoice" type="checkbox" value="vhs"> VHS</label>
-        </li>
-        <li>
-        <label for="media_2_1">
-        <input checked id="media_2_1" name="nestchoice" type="checkbox" value="dvd"> DVD
-        </label>
-        </li>
-        </ul></li>
-        </ul>
+        <div id="media">
+        <div> <label for="media_0">
+        <input type="checkbox" name="nestchoice" value="unknown" id="media_0"> Unknown</label></div>
+        <div>
+        <label>Audio</label>
+        <div> <label for="media_1_0">
+        <input checked type="checkbox" name="nestchoice" value="vinyl" id="media_1_0"> Vinyl</label></div>
+        <div> <label for="media_1_1">
+        <input type="checkbox" name="nestchoice" value="cd" id="media_1_1"> CD</label></div>
+        </div><div>
+        <label>Video</label>
+        <div> <label for="media_2_0">
+        <input type="checkbox" name="nestchoice" value="vhs" id="media_2_0"> VHS</label></div>
+        <div> <label for="media_2_1">
+        <input type="checkbox" name="nestchoice" value="dvd" id="media_2_1" checked> DVD</label></div>
+        </div>
+        </div>
         """
         """
         self.check_html(
         self.check_html(
             self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'),
             self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'),
@@ -90,31 +82,18 @@ class CheckboxSelectMultipleTest(WidgetTest):
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
         )
         )
         html = """
         html = """
-        <ul>
-        <li>
-        <label><input name="nestchoice" type="checkbox" value="unknown"> Unknown</label>
-        </li>
-        <li>Audio<ul>
-        <li>
-        <label>
-        <input checked name="nestchoice" type="checkbox" value="vinyl"> Vinyl
-        </label>
-        </li>
-        <li>
-        <label><input name="nestchoice" type="checkbox" value="cd"> CD</label>
-        </li>
-        </ul></li>
-        <li>Video<ul>
-        <li>
-        <label><input name="nestchoice" type="checkbox" value="vhs"> VHS</label>
-        </li>
-        <li>
-        <label>
-        <input checked name="nestchoice" type="checkbox" value="dvd"> DVD
-        </label>
-        </li>
-        </ul></li>
-        </ul>
+        <div>
+        <div> <label><input type="checkbox" name="nestchoice" value="unknown"> Unknown</label></div>
+        <div>
+        <label>Audio</label>
+        <div> <label><input checked type="checkbox" name="nestchoice" value="vinyl"> Vinyl</label></div>
+        <div> <label><input type="checkbox" name="nestchoice" value="cd"> CD</label></div>
+        </div><div>
+        <label>Video</label>
+        <div> <label><input type="checkbox" name="nestchoice" value="vhs"> VHS</label></div>
+        <div> <label><input type="checkbox" name="nestchoice" value="dvd"checked> DVD</label></div>
+        </div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'), html=html)
         self.check_html(self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'), html=html)
 
 
@@ -124,15 +103,15 @@ class CheckboxSelectMultipleTest(WidgetTest):
         """
         """
         choices = [('a', 'A'), ('b', 'B'), ('c', 'C')]
         choices = [('a', 'A'), ('b', 'B'), ('c', 'C')]
         html = """
         html = """
-        <ul id="abc">
-        <li>
+        <div id="abc">
+        <div>
         <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
         <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
-        </li>
-        <li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></li>
-        <li>
+        </div>
+        <div><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
+        <div>
         <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
         <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
-        </li>
-        </ul>
+        </div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=choices), 'letters', ['a', 'c'], attrs={'id': 'abc'}, html=html)
         self.check_html(self.widget(choices=choices), 'letters', ['a', 'c'], attrs={'id': 'abc'}, html=html)
 
 
@@ -142,15 +121,15 @@ class CheckboxSelectMultipleTest(WidgetTest):
         """
         """
         widget = CheckboxSelectMultiple(attrs={'id': 'abc'}, choices=[('a', 'A'), ('b', 'B'), ('c', 'C')])
         widget = CheckboxSelectMultiple(attrs={'id': 'abc'}, choices=[('a', 'A'), ('b', 'B'), ('c', 'C')])
         html = """
         html = """
-        <ul id="abc">
-        <li>
+        <div id="abc">
+        <div>
         <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
         <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
-        </li>
-        <li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></li>
-        <li>
+        </div>
+        <div><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
+        <div>
         <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
         <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
-        </li>
-        </ul>
+        </div>
+        </div>
         """
         """
         self.check_html(widget, 'letters', ['a', 'c'], html=html)
         self.check_html(widget, 'letters', ['a', 'c'], html=html)
 
 
@@ -162,11 +141,11 @@ class CheckboxSelectMultipleTest(WidgetTest):
             (1000000, 'One million'),
             (1000000, 'One million'),
         ]
         ]
         html = """
         html = """
-        <ul>
-        <li><label><input type="checkbox" name="numbers" value="1"> One</label></li>
-        <li><label><input type="checkbox" name="numbers" value="1000"> One thousand</label></li>
-        <li><label><input type="checkbox" name="numbers" value="1000000"> One million</label></li>
-        </ul>
+        <div>
+        <div><label><input type="checkbox" name="numbers" value="1"> One</label></div>
+        <div><label><input type="checkbox" name="numbers" value="1000"> One thousand</label></div>
+        <div><label><input type="checkbox" name="numbers" value="1000000"> One million</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=choices), 'numbers', None, html=html)
         self.check_html(self.widget(choices=choices), 'numbers', None, html=html)
 
 
@@ -175,10 +154,10 @@ class CheckboxSelectMultipleTest(WidgetTest):
             (datetime.time(12, 0), 'noon'),
             (datetime.time(12, 0), 'noon'),
         ]
         ]
         html = """
         html = """
-        <ul>
-        <li><label><input type="checkbox" name="times" value="00:00:00"> midnight</label></li>
-        <li><label><input type="checkbox" name="times" value="12:00:00"> noon</label></li>
-        </ul>
+        <div>
+        <div><label><input type="checkbox" name="times" value="00:00:00"> midnight</label></div>
+        <div><label><input type="checkbox" name="times" value="12:00:00"> noon</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=choices), 'times', None, html=html)
         self.check_html(self.widget(choices=choices), 'times', None, html=html)
 
 

+ 65 - 67
tests/forms_tests/widget_tests/test_radioselect.py

@@ -11,15 +11,15 @@ class RadioSelectTest(WidgetTest):
 
 
     def test_render(self):
     def test_render(self):
         choices = (('', '------'),) + self.beatles
         choices = (('', '------'),) + self.beatles
-        self.check_html(self.widget(choices=choices), 'beatle', 'J', html=(
-            """<ul>
-            <li><label><input type="radio" name="beatle" value=""> ------</label></li>
-            <li><label><input checked type="radio" name="beatle" value="J"> John</label></li>
-            <li><label><input type="radio" name="beatle" value="P"> Paul</label></li>
-            <li><label><input type="radio" name="beatle" value="G"> George</label></li>
-            <li><label><input type="radio" name="beatle" value="R"> Ringo</label></li>
-            </ul>"""
-        ))
+        self.check_html(self.widget(choices=choices), 'beatle', 'J', html="""
+            <div>
+            <div><label><input type="radio" name="beatle" value=""> ------</label></div>
+            <div><label><input checked type="radio" name="beatle" value="J"> John</label></div>
+            <div><label><input type="radio" name="beatle" value="P"> Paul</label></div>
+            <div><label><input type="radio" name="beatle" value="G"> George</label></div>
+            <div><label><input type="radio" name="beatle" value="R"> Ringo</label></div>
+            </div>
+        """)
 
 
     def test_nested_choices(self):
     def test_nested_choices(self):
         nested_choices = (
         nested_choices = (
@@ -28,25 +28,23 @@ class RadioSelectTest(WidgetTest):
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
             ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
         )
         )
         html = """
         html = """
-        <ul id="media">
-        <li>
-        <label for="media_0"><input id="media_0" name="nestchoice" type="radio" value="unknown"> Unknown</label>
-        </li>
-        <li>Audio<ul id="media_1">
-        <li>
-        <label for="media_1_0"><input id="media_1_0" name="nestchoice" type="radio" value="vinyl"> Vinyl</label>
-        </li>
-        <li><label for="media_1_1"><input id="media_1_1" name="nestchoice" type="radio" value="cd"> CD</label></li>
-        </ul></li>
-        <li>Video<ul id="media_2">
-        <li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="radio" value="vhs"> VHS</label></li>
-        <li>
-        <label for="media_2_1">
-        <input checked id="media_2_1" name="nestchoice" type="radio" value="dvd"> DVD
-        </label>
-        </li>
-        </ul></li>
-        </ul>
+        <div id="media">
+        <div>
+        <label for="media_0"><input type="radio" name="nestchoice" value="unknown" id="media_0"> Unknown</label></div>
+        <div>
+        <label>Audio</label>
+        <div>
+        <label for="media_1_0"><input type="radio" name="nestchoice" value="vinyl" id="media_1_0"> Vinyl</label></div>
+        <div> <label for="media_1_1"><input type="radio" name="nestchoice" value="cd" id="media_1_1"> CD</label></div>
+        </div><div>
+        <label>Video</label>
+        <div>
+        <label for="media_2_0"><input type="radio" name="nestchoice" value="vhs" id="media_2_0"> VHS</label></div>
+        <div>
+        <label for="media_2_1"><input type="radio" name="nestchoice" value="dvd" id="media_2_1" checked> DVD</label>
+        </div>
+        </div>
+        </div>
         """
         """
         self.check_html(
         self.check_html(
             self.widget(choices=nested_choices), 'nestchoice', 'dvd',
             self.widget(choices=nested_choices), 'nestchoice', 'dvd',
@@ -60,14 +58,14 @@ class RadioSelectTest(WidgetTest):
         """
         """
         widget = RadioSelect(attrs={'id': 'foo'}, choices=self.beatles)
         widget = RadioSelect(attrs={'id': 'foo'}, choices=self.beatles)
         html = """
         html = """
-        <ul id="foo">
-        <li>
+        <div id="foo">
+        <div>
         <label for="foo_0"><input checked type="radio" id="foo_0" value="J" name="beatle"> John</label>
         <label for="foo_0"><input checked type="radio" id="foo_0" value="J" name="beatle"> John</label>
-        </li>
-        <li><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle"> Paul</label></li>
-        <li><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle"> George</label></li>
-        <li><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle"> Ringo</label></li>
-        </ul>
+        </div>
+        <div><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle"> Paul</label></div>
+        <div><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle"> George</label></div>
+        <div><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle"> Ringo</label></div>
+        </div>
         """
         """
         self.check_html(widget, 'beatle', 'J', html=html)
         self.check_html(widget, 'beatle', 'J', html=html)
 
 
@@ -77,29 +75,29 @@ class RadioSelectTest(WidgetTest):
         inputs.
         inputs.
         """
         """
         html = """
         html = """
-        <ul id="bar">
-        <li>
+        <div id="bar">
+        <div>
         <label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle"> John</label>
         <label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle"> John</label>
-        </li>
-        <li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle"> Paul</label></li>
-        <li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle"> George</label></li>
-        <li><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle"> Ringo</label></li>
-        </ul>
+        </div>
+        <div><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle"> Paul</label></div>
+        <div><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle"> George</label></div>
+        <div><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle"> Ringo</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'id': 'bar'}, html=html)
         self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'id': 'bar'}, html=html)
 
 
     def test_class_attrs(self):
     def test_class_attrs(self):
         """
         """
-        The <ul> in the multiple_input.html widget template include the class
+        The <div> in the multiple_input.html widget template include the class
         attribute.
         attribute.
         """
         """
         html = """
         html = """
-        <ul class="bar">
-        <li><label><input checked type="radio" class="bar" value="J" name="beatle"> John</label></li>
-        <li><label><input type="radio" class="bar" value="P" name="beatle"> Paul</label></li>
-        <li><label><input type="radio" class="bar" value="G" name="beatle"> George</label></li>
-        <li><label><input type="radio" class="bar" value="R" name="beatle"> Ringo</label></li>
-        </ul>
+        <div class="bar">
+        <div><label><input checked type="radio" class="bar" value="J" name="beatle"> John</label></div>
+        <div><label><input type="radio" class="bar" value="P" name="beatle"> Paul</label></div>
+        <div><label><input type="radio" class="bar" value="G" name="beatle"> George</label></div>
+        <div><label><input type="radio" class="bar" value="R" name="beatle"> Ringo</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'class': 'bar'}, html=html)
         self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'class': 'bar'}, html=html)
 
 
@@ -111,11 +109,11 @@ class RadioSelectTest(WidgetTest):
             (1000000, 'One million'),
             (1000000, 'One million'),
         ]
         ]
         html = """
         html = """
-        <ul>
-        <li><label><input type="radio" name="number" value="1"> One</label></li>
-        <li><label><input type="radio" name="number" value="1000"> One thousand</label></li>
-        <li><label><input type="radio" name="number" value="1000000"> One million</label></li>
-        </ul>
+        <div>
+        <div><label><input type="radio" name="number" value="1"> One</label></div>
+        <div><label><input type="radio" name="number" value="1000"> One thousand</label></div>
+        <div><label><input type="radio" name="number" value="1000000"> One million</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=choices), 'number', None, html=html)
         self.check_html(self.widget(choices=choices), 'number', None, html=html)
 
 
@@ -124,22 +122,22 @@ class RadioSelectTest(WidgetTest):
             (datetime.time(12, 0), 'noon'),
             (datetime.time(12, 0), 'noon'),
         ]
         ]
         html = """
         html = """
-        <ul>
-        <li><label><input type="radio" name="time" value="00:00:00"> midnight</label></li>
-        <li><label><input type="radio" name="time" value="12:00:00"> noon</label></li>
-        </ul>
+        <div>
+        <div><label><input type="radio" name="time" value="00:00:00"> midnight</label></div>
+        <div><label><input type="radio" name="time" value="12:00:00"> noon</label></div>
+        </div>
         """
         """
         self.check_html(self.widget(choices=choices), 'time', None, html=html)
         self.check_html(self.widget(choices=choices), 'time', None, html=html)
 
 
     def test_render_as_subwidget(self):
     def test_render_as_subwidget(self):
         """A RadioSelect as a subwidget of MultiWidget."""
         """A RadioSelect as a subwidget of MultiWidget."""
         choices = (('', '------'),) + self.beatles
         choices = (('', '------'),) + self.beatles
-        self.check_html(MultiWidget([self.widget(choices=choices)]), 'beatle', ['J'], html=(
-            """<ul>
-            <li><label><input type="radio" name="beatle_0" value=""> ------</label></li>
-            <li><label><input checked type="radio" name="beatle_0" value="J"> John</label></li>
-            <li><label><input type="radio" name="beatle_0" value="P"> Paul</label></li>
-            <li><label><input type="radio" name="beatle_0" value="G"> George</label></li>
-            <li><label><input type="radio" name="beatle_0" value="R"> Ringo</label></li>
-            </ul>"""
-        ))
+        self.check_html(MultiWidget([self.widget(choices=choices)]), 'beatle', ['J'], html="""
+            <div>
+            <div><label><input type="radio" name="beatle_0" value=""> ------</label></div>
+            <div><label><input checked type="radio" name="beatle_0" value="J"> John</label></div>
+            <div><label><input type="radio" name="beatle_0" value="P"> Paul</label></div>
+            <div><label><input type="radio" name="beatle_0" value="G"> George</label></div>
+            <div><label><input type="radio" name="beatle_0" value="R"> Ringo</label></div>
+            </div>
+        """)

+ 13 - 13
tests/model_forms/test_modelchoicefield.py

@@ -294,14 +294,14 @@ class ModelChoiceFieldTests(TestCase):
         field = CustomModelMultipleChoiceField(Category.objects.all())
         field = CustomModelMultipleChoiceField(Category.objects.all())
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             field.widget.render('name', []), (
             field.widget.render('name', []), (
-                '<ul>'
-                '<li><label><input type="checkbox" name="name" value="%d" '
-                'data-slug="entertainment">Entertainment</label></li>'
-                '<li><label><input type="checkbox" name="name" value="%d" '
-                'data-slug="test">A test</label></li>'
-                '<li><label><input type="checkbox" name="name" value="%d" '
-                'data-slug="third-test">Third</label></li>'
-                '</ul>'
+                '<div>'
+                '<div><label><input type="checkbox" name="name" value="%d" '
+                'data-slug="entertainment">Entertainment</label></div>'
+                '<div><label><input type="checkbox" name="name" value="%d" '
+                'data-slug="test">A test</label></div>'
+                '<div><label><input type="checkbox" name="name" value="%d" '
+                'data-slug="third-test">Third</label></div>'
+                '</div>'
             ) % (self.c1.pk, self.c2.pk, self.c3.pk),
             ) % (self.c1.pk, self.c2.pk, self.c3.pk),
         )
         )
 
 
@@ -334,11 +334,11 @@ class ModelChoiceFieldTests(TestCase):
         field = CustomModelMultipleChoiceField(Category.objects.all())
         field = CustomModelMultipleChoiceField(Category.objects.all())
         self.assertHTMLEqual(
         self.assertHTMLEqual(
             field.widget.render('name', []),
             field.widget.render('name', []),
-            '''<ul>
-<li><label><input type="checkbox" name="name" value="%d" data-slug="entertainment">Entertainment</label></li>
-<li><label><input type="checkbox" name="name" value="%d" data-slug="test">A test</label></li>
-<li><label><input type="checkbox" name="name" value="%d" data-slug="third-test">Third</label></li>
-</ul>''' % (self.c1.pk, self.c2.pk, self.c3.pk),
+            """<div>
+<div><label><input type="checkbox" name="name" value="%d" data-slug="entertainment">Entertainment</label></div>
+<div><label><input type="checkbox" name="name" value="%d" data-slug="test">A test</label></div>
+<div><label><input type="checkbox" name="name" value="%d" data-slug="third-test">Third</label></div>
+</div>""" % (self.c1.pk, self.c2.pk, self.c3.pk),
         )
         )
 
 
     def test_choices_not_fetched_when_not_rendering(self):
     def test_choices_not_fetched_when_not_rendering(self):