Skip to content

Commit

Permalink
Fixed #20931 -- Fixed select widgets nested choice rendering
Browse files Browse the repository at this point in the history
ChoiceFieldRenderer was not rendering nested choices. Added recursion
to ChoiceFieldRenderer to take nested choices and render them as
<ul>'s.
  • Loading branch information
famousfilm authored and jphalip committed Sep 29, 2013
1 parent 5866a49 commit a834bc8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 6 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -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>
Expand Down
23 changes: 17 additions & 6 deletions django/forms/widgets.py
Expand Up @@ -665,10 +665,6 @@ def __init__(self, name, value, attrs, choices):
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)
Expand All @@ -685,8 +681,23 @@ def render(self):
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))

Expand Down
31 changes: 31 additions & 0 deletions tests/forms_tests/tests/test_widgets.py
Expand Up @@ -695,6 +695,37 @@ class CustomRadioSelect(RadioSelect):
<li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li>
<li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li>
<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):
Expand Down

0 comments on commit a834bc8

Please sign in to comment.