Commits

anti_social committed a08d73a

Added support optgroups for SelectField.

Comments (0)

Files changed (2)

 
 
 class SelectField(SelectFieldBase):
+    """
+    Represents select field that can be flat or with optgroups.
+    """
     widget = widgets.Select()
 
     def __init__(self, label=u'', validators=None, coerce=unicode, choices=None, **kwargs):
+        """
+        :param choices:
+            flat
+            choices=[(u'1', u'Value 1'), (u'2', u'Value 2')]
+
+            with optgroups
+            choices=[(u'Group 1', [(u'1', u'Value 1')]), (u'Group 2', [(u'2', u'Value 2'), (u'3', u'Value 3')])]
+        """
         super(SelectField, self).__init__(label, validators, **kwargs)
         self.coerce = coerce
         self.choices = choices
 
-    def iter_choices(self):
-        for value, label in self.choices:
-            yield (value, label, self.coerce(value) == self.data)
+    def iter_choices(self, choices=None):
+        choices = choices if choices is not None else self.choices
+        for value, label in choices:
+            if isinstance(label, (list, tuple)):
+                yield (value, self.iter_choices(label), None)
+            else:
+                yield (value, label, self.coerce(value) == self.data)
 
     def process_data(self, value):
         try:
                 raise ValueError(self.gettext(u'Invalid Choice: could not coerce'))
 
     def pre_validate(self, form):
-        for v, _ in self.choices:
-            if self.data == v:
-                break
+        for k, v in self.choices:
+            if isinstance(v, (list, tuple)):
+                for k2, v2 in v:
+                    if self.data == self.coerce(k2):
+                        return
+            elif self.data == self.coerce(k):
+                return
         else:
             raise ValueError(self.gettext(u'Not a valid choice'))
 
         kwargs.setdefault('id', field.id)
         return HTMLString(u'<textarea %s>%s</textarea>' % (html_params(name=field.name, **kwargs), escape(unicode(field._value()))))
 
-
 class Select(object):
     """
     Renders a select field.
         if self.multiple:
             kwargs['multiple'] = 'multiple'
         html = [u'<select %s>' % html_params(name=field.name, **kwargs)]
-        for val, label, selected in field.iter_choices():
-            html.append(self.render_option(val, label, selected))
+        for value, label, selected in field.iter_choices():
+            if hasattr(label, '__iter__'):
+                html.append(u'<optgroup %s>' % html_params(label=value))
+                for v, l, s in label:
+                    html.append(self.render_option(v, l, s))
+                html.append(u'</optgroup>')
+            else:
+                html.append(self.render_option(value, label, selected))
         html.append(u'</select>')
         return HTMLString(u''.join(html))
 
         options = {'value': value}
         if selected:
             options['selected'] = u'selected'
-        return HTMLString(u'<option %s>%s</option>' % (html_params(**options), escape(unicode(label))))
+        return HTMLString(u'<option %s>%s</option>' % (html_params(**options), escape(label)))
 
 
 class Option(object):