Commit fc90c09e authored by Adrian Holovaty's avatar Adrian Holovaty
Browse files

Made BoundFields iterable, so that you can iterate over individual radio...

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
parent 0519adb2
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
+19 −0
Original line number Diff line number Diff line
@@ -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 = ''
+36 −1
Original line number Diff line number Diff line
@@ -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``
~~~~~~~~~~~~~~~~~~~~~~~~~~

+22 −0
Original line number Diff line number Diff line
@@ -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):