summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Babiak <famousfilm@hotmail.com>2013-09-26 08:28:26 -0700
committerJulien Phalip <jphalip@gmail.com>2013-09-28 17:25:32 -0700
commita834bc84d8c76d4a7016ac4db4f631a8bcaa3d0b (patch)
tree03da95880324c1ba026807e87cdc38db905da08e
parent5866a49369cc6a4280b25a9d1834ceababf61d88 (diff)
Fixed #20931 -- Fixed select widgets nested choice rendering
ChoiceFieldRenderer was not rendering nested choices. Added recursion to ChoiceFieldRenderer to take nested choices and render them as <ul>'s.
-rw-r--r--AUTHORS1
-rw-r--r--django/forms/widgets.py23
-rw-r--r--tests/forms_tests/tests/test_widgets.py31
3 files changed, 49 insertions, 6 deletions
diff --git a/AUTHORS b/AUTHORS
index 7e620f89a6..75ad94ec1f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -89,6 +89,7 @@ answer newbie questions, and generally made Django that much better:
David Avsajanishvili <avsd05@gmail.com>
Mike Axiak <axiak@mit.edu>
Niran Babalola <niran@niran.org>
+ Christopher Babiak <chrisbabiak@gmail.com>
Vitaly Babiy <vbabiy86@gmail.com>
Morten Bagai <m@bagai.com>
Jeff Balogh <jbalogh@mozilla.com>
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 372c0f440b..f8320117f7 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -665,10 +665,6 @@ class ChoiceFieldRenderer(object):
self.attrs = attrs
self.choices = choices
- def __iter__(self):
- for i, choice in enumerate(self.choices):
- yield self.choice_input_class(self.name, self.value, self.attrs.copy(), choice, i)
-
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return self.choice_input_class(self.name, self.value, self.attrs.copy(), choice, idx)
@@ -685,8 +681,23 @@ class ChoiceFieldRenderer(object):
id_ = self.attrs.get('id', None)
start_tag = format_html('<ul id="{0}">', id_) if id_ else '<ul>'
output = [start_tag]
- for widget in self:
- output.append(format_html('<li>{0}</li>', force_text(widget)))
+ for i, choice in enumerate(self.choices):
+ choice_value, choice_label = choice
+ if isinstance(choice_label, (tuple,list)):
+ attrs_plus = self.attrs.copy()
+ if id_:
+ attrs_plus['id'] += '_{0}'.format(i)
+ sub_ul_renderer = ChoiceFieldRenderer(name=self.name,
+ value=self.value,
+ attrs=attrs_plus,
+ choices=choice_label)
+ sub_ul_renderer.choice_input_class = self.choice_input_class
+ output.append(format_html('<li>{0}{1}</li>', choice_value,
+ sub_ul_renderer.render()))
+ else:
+ w = self.choice_input_class(self.name, self.value,
+ self.attrs.copy(), choice, i)
+ output.append(format_html('<li>{0}</li>', force_text(w)))
output.append('</ul>')
return mark_safe('\n'.join(output))
diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py
index 033bc7a035..2d667791e8 100644
--- a/tests/forms_tests/tests/test_widgets.py
+++ b/tests/forms_tests/tests/test_widgets.py
@@ -697,6 +697,37 @@ beatle J R Ringo False""")
<li><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li>
</ul>""")
+ def test_nested_choices(self):
+ # Choices can be nested for radio buttons:
+ w = RadioSelect()
+ w.choices=(('unknown', 'Unknown'), ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))))
+ self.assertHTMLEqual(w.render('nestchoice', 'dvd', attrs={'id':'media'}), """<ul id="media">
+<li><label for="media_0"><input id="media_0" name="nestchoice" type="radio" value="unknown" /> Unknown</label></li>
+<li>Audio<ul id="media_1">
+<li><label for="media_1_0"><input id="media_1_0" name="nestchoice" type="radio" value="vinyl" /> Vinyl</label></li>
+<li><label for="media_1_1"><input id="media_1_1" name="nestchoice" type="radio" value="cd" /> CD</label></li>
+</ul></li>
+<li>Video<ul id="media_2">
+<li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="radio" value="vhs" /> VHS</label></li>
+<li><label for="media_2_1"><input checked="checked" id="media_2_1" name="nestchoice" type="radio" value="dvd" /> DVD</label></li>
+</ul></li>
+</ul>""")
+
+ # Choices can be nested for checkboxes:
+ w = CheckboxSelectMultiple()
+ w.choices=(('unknown', 'Unknown'), ('Audio', (('vinyl', 'Vinyl'), ('cd', 'CD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))))
+ self.assertHTMLEqual(w.render('nestchoice', ('vinyl', 'dvd'), attrs={'id':'media'}), """<ul id="media">
+<li><label for="media_0"><input id="media_0" name="nestchoice" type="checkbox" value="unknown" /> Unknown</label></li>
+<li>Audio<ul id="media_1">
+<li><label for="media_1_0"><input checked="checked" id="media_1_0" name="nestchoice" type="checkbox" value="vinyl" /> Vinyl</label></li>
+<li><label for="media_1_1"><input id="media_1_1" name="nestchoice" type="checkbox" value="cd" /> CD</label></li>
+</ul></li>
+<li>Video<ul id="media_2">
+<li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="checkbox" value="vhs" /> VHS</label></li>
+<li><label for="media_2_1"><input checked="checked" id="media_2_1" name="nestchoice" type="checkbox" value="dvd" /> DVD</label></li>
+</ul></li>
+</ul>""")
+
def test_checkboxselectmultiple(self):
w = CheckboxSelectMultiple()
self.assertHTMLEqual(w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), """<ul>