diff options
| author | Jon Dufresne <jon.dufresne@gmail.com> | 2016-07-21 17:16:22 -0700 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-08-01 10:36:17 -0400 |
| commit | ac3aaaa740dcf9c6efd2f88ee9219c1924c7695e (patch) | |
| tree | 72e8a094b6f88f60e80397eaf00ce52feb46ed6f | |
| parent | aad46c3e370e105f9117a337924090d05f1b001d (diff) | |
Fixed #26927 -- Made subwidget iteration pass disabled and required attributes.
| -rw-r--r-- | django/forms/boundfield.py | 16 | ||||
| -rw-r--r-- | django/forms/widgets.py | 7 | ||||
| -rw-r--r-- | docs/ref/forms/widgets.txt | 6 | ||||
| -rw-r--r-- | tests/forms_tests/tests/test_forms.py | 38 |
4 files changed, 42 insertions, 25 deletions
diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py index 4867e72deb..df3de5848e 100644 --- a/django/forms/boundfield.py +++ b/django/forms/boundfield.py @@ -51,6 +51,7 @@ class BoundField(object): """ id_ = self.field.widget.attrs.get('id') or self.auto_id attrs = {'id': id_} if id_ else {} + attrs = self.build_widget_attrs(attrs) for subwidget in self.field.widget.subwidgets(self.html_name, self.value(), attrs): yield subwidget @@ -85,10 +86,7 @@ class BoundField(object): widget.is_localized = True 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: - attrs['disabled'] = True + attrs = self.build_widget_attrs(attrs, widget) auto_id = self.auto_id if auto_id and 'id' not in attrs and 'id' not in widget.attrs: if not only_initial: @@ -227,3 +225,13 @@ class BoundField(object): widget = self.field.widget id_ = widget.attrs.get('id') or self.auto_id return widget.id_for_label(id_) + + def build_widget_attrs(self, attrs, widget=None): + if not widget: + widget = self.field.widget + attrs = dict(attrs) # Copy attrs to avoid modifying the argument. + if not widget.is_hidden and self.field.required and self.form.use_required_attribute: + attrs['required'] = True + if self.field.disabled: + attrs['disabled'] = True + return attrs diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 604a6a401a..217f37b022 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -787,6 +787,13 @@ class CheckboxSelectMultiple(RendererMixin, SelectMultiple): renderer = CheckboxFieldRenderer _empty_value = [] + def build_attrs(self, extra_attrs=None, **kwargs): + attrs = super(CheckboxSelectMultiple, self).build_attrs(extra_attrs, **kwargs) + # Remove the 'required' attribute because browser validation would + # require all checkboxes to be checked instead of at least one. + attrs.pop('required', None) + return attrs + class MultiWidget(Widget): """ diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index 6a8cdf3c70..aae92a37c9 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -714,8 +714,10 @@ Selector and checkbox widgets The outer ``<ul>`` container receives the ``id`` attribute of the widget, if defined, or :attr:`BoundField.auto_id` otherwise. -Like :class:`RadioSelect`, you can now loop over the individual checkboxes making -up the lists. See the documentation of :class:`RadioSelect` for more details. +Like :class:`RadioSelect`, you can loop over the individual checkboxes for the +widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the +``required`` HTML attribute if the field is required because browser validation +would require all checkboxes to be checked instead of at least one. When looping over the checkboxes, the ``label`` and ``input`` tags include ``for`` and ``id`` attributes, respectively. Each checkbox has an diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 2bd49b7361..a9c3c74b69 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -646,9 +646,9 @@ Java</label></li> self.assertHTMLEqual( t.render(Context({'form': f})), """<div class="myradio"><label for="id_language_0"> -<input id="id_language_0" name="language" type="radio" value="P" /> Python</label></div> +<input id="id_language_0" name="language" type="radio" value="P" required /> Python</label></div> <div class="myradio"><label for="id_language_1"> -<input id="id_language_1" name="language" type="radio" value="J" /> Java</label></div>""" +<input id="id_language_1" name="language" type="radio" value="J" required /> Java</label></div>""" ) def test_form_with_iterable_boundfield(self): @@ -661,17 +661,17 @@ Java</label></li> f = BeatleForm(auto_id=False) self.assertHTMLEqual( '\n'.join(str(bf) for bf in f['name']), - """<label><input type="radio" name="name" value="john" /> John</label> -<label><input type="radio" name="name" value="paul" /> Paul</label> -<label><input type="radio" name="name" value="george" /> George</label> -<label><input type="radio" name="name" value="ringo" /> Ringo</label>""" + """<label><input type="radio" name="name" value="john" required /> John</label> +<label><input type="radio" name="name" value="paul" required /> Paul</label> +<label><input type="radio" name="name" value="george" required /> George</label> +<label><input type="radio" name="name" value="ringo" required /> Ringo</label>""" ) self.assertHTMLEqual( '\n'.join('<div>%s</div>' % bf for bf in f['name']), - """<div><label><input type="radio" name="name" value="john" /> John</label></div> -<div><label><input type="radio" name="name" value="paul" /> Paul</label></div> -<div><label><input type="radio" name="name" value="george" /> George</label></div> -<div><label><input type="radio" name="name" value="ringo" /> Ringo</label></div>""" + """<div><label><input type="radio" name="name" value="john" required /> John</label></div> +<div><label><input type="radio" name="name" value="paul" required /> Paul</label></div> +<div><label><input type="radio" name="name" value="george" required /> George</label></div> +<div><label><input type="radio" name="name" value="ringo" required /> Ringo</label></div>""" ) def test_form_with_noniterable_boundfield(self): @@ -680,7 +680,7 @@ Java</label></li> name = CharField() f = BeatleForm(auto_id=False) - self.assertHTMLEqual('\n'.join(str(bf) for bf in f['name']), '<input type="text" name="name" />') + self.assertHTMLEqual('\n'.join(str(bf) for bf in f['name']), '<input type="text" name="name" required />') def test_boundfield_slice(self): class BeatleForm(Form): @@ -803,18 +803,18 @@ Java</label></li> f = SongForm(auto_id=False) self.assertHTMLEqual(str(f['composers']), """<ul> -<li><label><input type="checkbox" name="composers" value="J" required /> John Lennon</label></li> -<li><label><input type="checkbox" name="composers" value="P" required /> Paul McCartney</label></li> +<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>""") f = SongForm({'composers': ['J']}, auto_id=False) self.assertHTMLEqual(str(f['composers']), """<ul> -<li><label><input checked="checked" type="checkbox" name="composers" value="J" required /> John Lennon</label></li> -<li><label><input type="checkbox" name="composers" value="P" required /> Paul McCartney</label></li> +<li><label><input checked="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>""") f = SongForm({'composers': ['J', 'P']}, auto_id=False) self.assertHTMLEqual(str(f['composers']), """<ul> -<li><label><input checked="checked" type="checkbox" name="composers" value="J" required /> John Lennon</label></li> -<li><label><input checked="checked" type="checkbox" name="composers" value="P" required /> Paul McCartney</label></li> +<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li> +<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li> </ul>""") # Test iterating on individual checkboxes in a template t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}') @@ -839,9 +839,9 @@ Java</label></li> str(f['composers']), """<ul id="composers_id"> <li><label for="composers_id_0"> -<input type="checkbox" name="composers" value="J" id="composers_id_0" required /> John Lennon</label></li> +<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" required /> Paul McCartney</label></li> +<input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li> </ul>""" ) |
