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
This commit is contained in:
parent
0519adb2a8
commit
fc90c09efd
|
@ -410,6 +410,16 @@ class BoundField(StrAndUnicode):
|
||||||
return self.as_widget() + self.as_hidden(only_initial=True)
|
return self.as_widget() + self.as_hidden(only_initial=True)
|
||||||
return self.as_widget()
|
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):
|
def _errors(self):
|
||||||
"""
|
"""
|
||||||
Returns an ErrorList for this field. Returns an empty ErrorList
|
Returns an ErrorList for this field. Returns an empty ErrorList
|
||||||
|
|
|
@ -156,6 +156,15 @@ class Widget(object):
|
||||||
memo[id(self)] = obj
|
memo[id(self)] = obj
|
||||||
return 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):
|
def render(self, name, value, attrs=None):
|
||||||
"""
|
"""
|
||||||
Returns this Widget rendered as HTML, as a Unicode string.
|
Returns this Widget rendered as HTML, as a Unicode string.
|
||||||
|
@ -628,6 +637,12 @@ class RadioInput(StrAndUnicode):
|
||||||
self.index = index
|
self.index = index
|
||||||
|
|
||||||
def __unicode__(self):
|
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:
|
if 'id' in self.attrs:
|
||||||
label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
|
label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
|
||||||
else:
|
else:
|
||||||
|
@ -681,6 +696,10 @@ class RadioSelect(Select):
|
||||||
self.renderer = renderer
|
self.renderer = renderer
|
||||||
super(RadioSelect, self).__init__(*args, **kwargs)
|
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=()):
|
def get_renderer(self, name, value, attrs=None, choices=()):
|
||||||
"""Returns an instance of the renderer."""
|
"""Returns an instance of the renderer."""
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
|
|
|
@ -345,7 +345,8 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
.. class:: RadioSelect
|
.. 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
|
.. code-block:: html
|
||||||
|
|
||||||
|
@ -354,6 +355,40 @@ commonly used groups of widgets:
|
||||||
...
|
...
|
||||||
</ul>
|
</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``
|
``CheckboxSelectMultiple``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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>
|
<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
|
||||||
</ul></p>""")
|
</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):
|
def test_forms_wit_hmultiple_choice(self):
|
||||||
# MultipleChoiceField is a special case, as its data is required to be a list:
|
# MultipleChoiceField is a special case, as its data is required to be a list:
|
||||||
class SongForm(Form):
|
class SongForm(Form):
|
||||||
|
|
Loading…
Reference in New Issue