Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Made BoundFields iterable, so that you can iterate over individual ra…

…dio buttons of a RadioSelect in a template

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17173 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit fc90c09efdfbc029d43e6d4c0952ed6b49f6f34d 1 parent 0519adb
@adrianholovaty adrianholovaty authored
View
10 django/forms/forms.py
@@ -410,6 +410,16 @@ def __unicode__(self):
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
View
19 django/forms/widgets.py
@@ -156,6 +156,15 @@ def __deepcopy__(self, memo):
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 @@ def __init__(self, name, value, attrs, choice, index):
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 @@ def __init__(self, *args, **kwargs):
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 = ''
View
37 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``
~~~~~~~~~~~~~~~~~~~~~~~~~~
View
22 tests/regressiontests/forms/tests/forms.py
@@ -434,6 +434,28 @@ class FrameworkForm(Form):
<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):
Please sign in to comment.
Something went wrong with that request. Please try again.