summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2011-12-07 22:31:39 +0000
committerAdrian Holovaty <adrian@holovaty.com>2011-12-07 22:31:39 +0000
commitfc90c09efdfbc029d43e6d4c0952ed6b49f6f34d (patch)
treeb86bdd032d4afc93cb3135312377cf02532dc68d
parent0519adb2a87090bd9ee3f61c7b9a6ff6de5fa341 (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.py10
-rw-r--r--django/forms/widgets.py19
-rw-r--r--docs/ref/forms/widgets.txt37
-rw-r--r--tests/regressiontests/forms/tests/forms.py22
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):