diff options
| author | Adrian Holovaty <adrian@holovaty.com> | 2011-12-07 22:31:39 +0000 |
|---|---|---|
| committer | Adrian Holovaty <adrian@holovaty.com> | 2011-12-07 22:31:39 +0000 |
| commit | fc90c09efdfbc029d43e6d4c0952ed6b49f6f34d (patch) | |
| tree | b86bdd032d4afc93cb3135312377cf02532dc68d | |
| parent | 0519adb2a87090bd9ee3f61c7b9a6ff6de5fa341 (diff) | |
Made BoundFields iterable, so that you can iterate over individual radio buttons of a RadioSelect in a template
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17173 bcc190cf-cafb-0310-a4f2-bffc1f526a37
| -rw-r--r-- | django/forms/forms.py | 10 | ||||
| -rw-r--r-- | django/forms/widgets.py | 19 | ||||
| -rw-r--r-- | docs/ref/forms/widgets.txt | 37 | ||||
| -rw-r--r-- | tests/regressiontests/forms/tests/forms.py | 22 |
4 files changed, 87 insertions, 1 deletions
diff --git a/django/forms/forms.py b/django/forms/forms.py index 1400be3014..7818aac13a 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -410,6 +410,16 @@ class BoundField(StrAndUnicode): return self.as_widget() + self.as_hidden(only_initial=True) return self.as_widget() + def __iter__(self): + """ + Yields rendered strings that comprise all widgets in this BoundField. + + This really is only useful for RadioSelect widgets, so that you can + iterate over individual radio buttons in a template. + """ + for subwidget in self.field.widget.subwidgets(self.html_name, self.value()): + yield self.as_widget(subwidget) + def _errors(self): """ Returns an ErrorList for this field. Returns an empty ErrorList diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 57a6b4566a..9c04750fdb 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -156,6 +156,15 @@ class Widget(object): memo[id(self)] = obj return obj + def subwidgets(self, name, value, attrs=None, choices=()): + """ + Yields all "subwidgets" of this widget. Used only by RadioSelect to + allow template access to individual <input type="radio"> buttons. + + Arguments are the same as for render(). + """ + yield self + def render(self, name, value, attrs=None): """ Returns this Widget rendered as HTML, as a Unicode string. @@ -628,6 +637,12 @@ class RadioInput(StrAndUnicode): self.index = index def __unicode__(self): + return self.render() + + def render(self, name=None, value=None, attrs=None, choices=()): + name = name or self.name + value = value or self.value + attrs = attrs or self.attrs if 'id' in self.attrs: label_for = ' for="%s_%s"' % (self.attrs['id'], self.index) else: @@ -681,6 +696,10 @@ class RadioSelect(Select): self.renderer = renderer super(RadioSelect, self).__init__(*args, **kwargs) + def subwidgets(self, name, value, attrs=None, choices=()): + for widget in self.get_renderer(name, value, attrs, choices): + yield widget + def get_renderer(self, name, value, attrs=None, choices=()): """Returns an instance of the renderer.""" if value is None: value = '' diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index 2b386c0864..b657be4c53 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -345,7 +345,8 @@ commonly used groups of widgets: .. class:: RadioSelect - Similar to :class:`Select`, but rendered as a list of radio buttons: + Similar to :class:`Select`, but rendered as a list of radio buttons within + ``<li>`` tags: .. code-block:: html @@ -354,6 +355,40 @@ commonly used groups of widgets: ... </ul> + .. versionadded:: 1.4 + + 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 + ``beatles`` that uses a ``RadioSelect`` as its widget: + + .. code-block:: html+django + + {% for radio in myform.beatles %} + <div class="myradio"> + {{ radio }} + </div> + {% endfor %} + + This would generate the following HTML: + + .. code-block:: html + + <div class="myradio"> + <label><input type="radio" name="beatles" value="john" /> John</label> + </div> + <div class="myradio"> + <label><input type="radio" name="beatles" value="paul" /> Paul</label> + </div> + <div class="myradio"> + <label><input type="radio" name="beatles" value="george" /> George</label> + </div> + <div class="myradio"> + <label><input type="radio" name="beatles" value="ringo" /> Ringo</label> + </div> + + If you decide not to loop over the radio buttons, they'll be output in a + ``<ul>`` with ``<li>`` tags, as above. + ``CheckboxSelectMultiple`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/regressiontests/forms/tests/forms.py b/tests/regressiontests/forms/tests/forms.py index a37cc2e3cb..3cedb04b94 100644 --- a/tests/regressiontests/forms/tests/forms.py +++ b/tests/regressiontests/forms/tests/forms.py @@ -434,6 +434,28 @@ class FormsTestCase(TestCase): <li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li> </ul></p>""") + def test_form_with_iterable_boundfield(self): + class BeatleForm(Form): + name = ChoiceField(choices=[('john', 'John'), ('paul', 'Paul'), ('george', 'George'), ('ringo', 'Ringo')], widget=RadioSelect) + + f = BeatleForm(auto_id=False) + self.assertEqual('\n'.join(list(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>""") + self.assertEqual('\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>""") + + def test_form_with_noniterable_boundfield(self): + # You can iterate over any BoundField, not just those with widget=RadioSelect. + class BeatleForm(Form): + name = CharField() + + f = BeatleForm(auto_id=False) + self.assertEqual('\n'.join(list(f['name'])), u'<input type="text" name="name" />') + def test_forms_wit_hmultiple_choice(self): # MultipleChoiceField is a special case, as its data is required to be a list: class SongForm(Form): |
