Skip to content

Commit

Permalink
Changed BoundField.subwidgets() to return SubWidget objects instead o…
Browse files Browse the repository at this point in the history
…f rendered strings. This means we can access individual radio buttons' properties in the template (see new docs)

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17175 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
adrianholovaty committed Dec 7, 2011
1 parent 0920165 commit 08bec4f
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 7 deletions.
8 changes: 7 additions & 1 deletion django/forms/forms.py
Expand Up @@ -418,7 +418,13 @@ def __iter__(self):
iterate over individual radio buttons in a template. iterate over individual radio buttons in a template.
""" """
for subwidget in self.field.widget.subwidgets(self.html_name, self.value()): for subwidget in self.field.widget.subwidgets(self.html_name, self.value()):
yield self.as_widget(subwidget) yield subwidget

def __len__(self):
return len(list(self.__iter__()))

def __getitem__(self, idx):
return list(self.__iter__())[idx]


def _errors(self): def _errors(self):
""" """
Expand Down
20 changes: 18 additions & 2 deletions django/forms/widgets.py
Expand Up @@ -137,6 +137,22 @@ def __new__(cls, name, bases, attrs):
new_class.media = media_property(new_class) new_class.media = media_property(new_class)
return new_class return new_class


class SubWidget(StrAndUnicode):
"""
Some widgets are made of multiple HTML elements -- namely, RadioSelect.
This is a class that represents the "inner" HTML element of a widget.
"""
def __init__(self, parent_widget, name, value, attrs, choices):
self.parent_widget = parent_widget
self.name, self.value = name, value
self.attrs, self.choices = attrs, choices

def __unicode__(self):
args = [self.name, self.value, self.attrs]
if self.choices:
args.append(self.choices)
return self.parent_widget.render(*args)

class Widget(object): class Widget(object):
__metaclass__ = MediaDefiningClass __metaclass__ = MediaDefiningClass
is_hidden = False # Determines whether this corresponds to an <input type="hidden">. is_hidden = False # Determines whether this corresponds to an <input type="hidden">.
Expand All @@ -163,7 +179,7 @@ def subwidgets(self, name, value, attrs=None, choices=()):
Arguments are the same as for render(). Arguments are the same as for render().
""" """
yield self yield SubWidget(self, name, value, attrs, choices)


def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
""" """
Expand Down Expand Up @@ -623,7 +639,7 @@ def _has_changed(self, initial, data):
data_set = set([force_unicode(value) for value in data]) data_set = set([force_unicode(value) for value in data])
return data_set != initial_set return data_set != initial_set


class RadioInput(StrAndUnicode): class RadioInput(SubWidget):
""" """
An object used by RadioFieldRenderer that represents a single An object used by RadioFieldRenderer that represents a single
<input type='radio'>. <input type='radio'>.
Expand Down
37 changes: 35 additions & 2 deletions docs/ref/forms/widgets.txt
Expand Up @@ -386,8 +386,41 @@ commonly used groups of widgets:
<label><input type="radio" name="beatles" value="ringo" /> Ringo</label> <label><input type="radio" name="beatles" value="ringo" /> Ringo</label>
</div> </div>


If you decide not to loop over the radio buttons, they'll be output in a That included the ``<label>`` tags. To get more granular, you can use each
``<ul>`` with ``<li>`` tags, as above. radio button's ``tag`` and ``choice_label`` attributes. For example, this template...

.. code-block:: html+django

{% for radio in myform.beatles %}
<label>
{{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span>
</label>
{% endfor %}

...will result in the following HTML:

.. code-block:: html

<label>
John
<span class="radio"><input type="radio" name="beatles" value="john" /></span>
</label>
<label>
Paul
<span class="radio"><input type="radio" name="beatles" value="paul" /></span>
</label>
<label>
George
<span class="radio"><input type="radio" name="beatles" value="george" /></span>
</label>
<label>
Ringo
<span class="radio"><input type="radio" name="beatles" value="ringo" /></span>
</label>

If you decide not to loop over the radio buttons -- e.g., if your template simply includes
``{{ myform.beatles }}`` -- they'll be output in a ``<ul>`` with ``<li>`` tags, as above.


``CheckboxSelectMultiple`` ``CheckboxSelectMultiple``
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
4 changes: 2 additions & 2 deletions tests/regressiontests/forms/tests/forms.py
Expand Up @@ -439,7 +439,7 @@ class BeatleForm(Form):
name = ChoiceField(choices=[('john', 'John'), ('paul', 'Paul'), ('george', 'George'), ('ringo', 'Ringo')], widget=RadioSelect) name = ChoiceField(choices=[('john', 'John'), ('paul', 'Paul'), ('george', 'George'), ('ringo', 'Ringo')], widget=RadioSelect)


f = BeatleForm(auto_id=False) f = BeatleForm(auto_id=False)
self.assertEqual('\n'.join(list(f['name'])), """<label><input type="radio" name="name" value="john" /> John</label> self.assertEqual('\n'.join([str(bf) for bf in 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="paul" /> Paul</label>
<label><input type="radio" name="name" value="george" /> George</label> <label><input type="radio" name="name" value="george" /> George</label>
<label><input type="radio" name="name" value="ringo" /> Ringo</label>""") <label><input type="radio" name="name" value="ringo" /> Ringo</label>""")
Expand All @@ -454,7 +454,7 @@ class BeatleForm(Form):
name = CharField() name = CharField()


f = BeatleForm(auto_id=False) f = BeatleForm(auto_id=False)
self.assertEqual('\n'.join(list(f['name'])), u'<input type="text" name="name" />') self.assertEqual('\n'.join([str(bf) for bf in f['name']]), u'<input type="text" name="name" />')


def test_forms_with_multiple_choice(self): def test_forms_with_multiple_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:
Expand Down

0 comments on commit 08bec4f

Please sign in to comment.