| 68 | |
| 69 | class GroupedSelect(Select): |
| 70 | def __init__(self, attrs=None, groups=(), choices=()): |
| 71 | super(GroupedSelect, self).__init__(attrs, choices) |
| 72 | # groups maps from 'group name' to a list of values in that group |
| 73 | # to preserve ordering, it's a list-of-tuples instead of a dict |
| 74 | self.groups = groups |
| 75 | |
| 76 | def render(self, name, value, attrs=None, choices=()): |
| 77 | if value is None: value = '' |
| 78 | final_attrs = self.build_attrs(attrs, name=name) |
| 79 | output = [u'<select%s>' % flatatt(final_attrs)] |
| 80 | str_value = force_unicode(value) # Normalize to string. |
| 81 | allchoices = dict([(force_unicode(k), force_unicode(v)) |
| 82 | for k, v in chain(self.choices, choices)]) |
| 83 | for group_label, members in self.groups: |
| 84 | group_label = force_unicode(group_label) |
| 85 | output.append(u'<optgroup label="%s">' % escape(group_label)) |
| 86 | for option_value in members: |
| 87 | option_value = force_unicode(option_value) |
| 88 | try: |
| 89 | option_label = allchoices[option_value] |
| 90 | except KeyError: |
| 91 | continue # silently ignore missing elements from the group |
| 92 | selected_html = (option_value == str_value) and u' selected="selected"' or '' |
| 93 | output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(option_label))) |
| 94 | output.append(u'</optgroup>') |
| 95 | output.append(u'</select>') |
| 96 | return mark_safe(u'\n'.join(output)) |