Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py (revision 5400)
+++ django/db/models/base.py (working copy)
@@ -317,7 +317,13 @@
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
- return dict(field.choices).get(value, value)
+ newchoices = []
+ for choice in field.choices:
+ if type(choice[1]) == list or type(choice[1]) == tuple:
+ newchoices.extend(choice[1])
+ else:
+ newchoices.append(choice)
+ return dict(newchoices).get(value, value)
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
op = is_next and '>' or '<'
Index: tests/regressiontests/forms/tests.py
===================================================================
--- tests/regressiontests/forms/tests.py (revision 5400)
+++ tests/regressiontests/forms/tests.py (working copy)
@@ -392,6 +392,33 @@
+Choices can be nested one level in order to create HTML optgroups:
+>>> w = Select(choices=(('outer1', 'Outer 1'), ('Group 1', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))))
+>>> print w.render('nestchoice', None)
+
+>>> print w.render('nestchoice', 'outer1')
+
+>>> print w.render('nestchoice', 'inner1')
+
+
# NullBooleanSelect Widget ####################################################
>>> w = NullBooleanSelect()
@@ -533,6 +560,41 @@
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u''
+Choices can be nested one level in order to create HTML optgroups:
+>>> w = SelectMultiple(choices=(('outer1', 'Outer 1'), ('Group 1', (('inner1', 'Inner 1'), ('inner2', 'Inner 2')))))
+>>> print w.render('nestchoice', None)
+
+>>> print w.render('nestchoice', ['outer1'])
+
+>>> print w.render('nestchoice', ['inner1'])
+
+>>> print w.render('nestchoice', ['outer1', 'inner2'])
+
+
# RadioSelect Widget ##########################################################
>>> w = RadioSelect()
Index: django/newforms/fields.py
===================================================================
--- django/newforms/fields.py (revision 5400)
+++ django/newforms/fields.py (working copy)
@@ -427,7 +427,14 @@
value = smart_unicode(value)
if value == u'':
return value
- valid_values = set([str(k) for k, v in self.choices])
+ valid_values = []
+ for k, v in self.choices:
+ if type(v) == tuple or type(v) == list:
+ for k2, v2 in v:
+ valid_values.append(str(k2))
+ continue
+ valid_values.append(str(k))
+ valid_values = set(valid_values)
if value not in valid_values:
raise ValidationError(gettext(u'Select a valid choice. That choice is not one of the available choices.'))
return value
@@ -451,7 +458,14 @@
val = smart_unicode(val)
new_value.append(val)
# Validate that each value in the value list is in self.choices.
- valid_values = set([smart_unicode(k) for k, v in self.choices])
+ valid_values = []
+ for k, v in self.choices:
+ if type(v) == tuple or type(v) == list:
+ for k2, v2 in v:
+ valid_values.append(smart_unicode(k2))
+ continue
+ valid_values.append(smart_unicode(k))
+ valid_values = set(valid_values)
for val in new_value:
if val not in valid_values:
raise ValidationError(gettext(u'Select a valid choice. %s is not one of the available choices.') % val)
Index: django/newforms/widgets.py
===================================================================
--- django/newforms/widgets.py (revision 5400)
+++ django/newforms/widgets.py (working copy)
@@ -168,6 +168,14 @@
output = [u'