summaryrefslogtreecommitdiff
path: root/django/forms
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2008-07-19 07:53:02 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2008-07-19 07:53:02 +0000
commit649463dd348abd6d0cab890e2372e88fc452128e (patch)
treef554f6163a30f9d0dfdb996a8322ac73d565a999 /django/forms
parentb5b0febc4cd5ad51aeb1ef7b37aaca6a7632519d (diff)
Fixed #4412 -- Added support for optgroups, both in the model when defining choices, and in the form field and widgets when the optgroups are displayed. Thanks to Matt McClanahan <cardinal@dodds.net>, Tai Lee <real.human@mrmachine.net> and SmileyChris for their contributions at various stages in the life of this ticket.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7977 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/forms')
-rw-r--r--django/forms/fields.py21
-rw-r--r--django/forms/widgets.py52
2 files changed, 45 insertions, 28 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py
index 77bd8cbe94..9df8955392 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -585,7 +585,7 @@ class NullBooleanField(BooleanField):
class ChoiceField(Field):
widget = Select
default_error_messages = {
- 'invalid_choice': _(u'Select a valid choice. That choice is not one of the available choices.'),
+ 'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'),
}
def __init__(self, choices=(), required=True, widget=None, label=None,
@@ -615,11 +615,23 @@ class ChoiceField(Field):
value = smart_unicode(value)
if value == u'':
return value
- valid_values = set([smart_unicode(k) for k, v in self.choices])
- if value not in valid_values:
+ if not self.valid_value(value):
raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
return value
+ def valid_value(self, value):
+ "Check to see if the provided value is a valid choice"
+ for k, v in self.choices:
+ if type(v) in (tuple, list):
+ # This is an optgroup, so look inside the group for options
+ for k2, v2 in v:
+ if value == smart_unicode(k2):
+ return True
+ else:
+ if value == smart_unicode(k):
+ return True
+ return False
+
class MultipleChoiceField(ChoiceField):
hidden_widget = MultipleHiddenInput
widget = SelectMultiple
@@ -640,9 +652,8 @@ class MultipleChoiceField(ChoiceField):
raise ValidationError(self.error_messages['invalid_list'])
new_value = [smart_unicode(val) for val in value]
# Validate that each value in the value list is in self.choices.
- valid_values = set([smart_unicode(k) for k, v in self.choices])
for val in new_value:
- if val not in valid_values:
+ if not self.valid_value(val):
raise ValidationError(self.error_messages['invalid_choice'] % {'value': val})
return new_value
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 2c9f3c2eba..cc065d71d8 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -345,17 +345,32 @@ class Select(Widget):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select%s>' % flatatt(final_attrs)]
- # Normalize to string.
- str_value = force_unicode(value)
- for option_value, option_label in chain(self.choices, choices):
- option_value = force_unicode(option_value)
- selected_html = (option_value == str_value) and u' selected="selected"' or ''
- output.append(u'<option value="%s"%s>%s</option>' % (
- escape(option_value), selected_html,
- conditional_escape(force_unicode(option_label))))
- output.append(u'</select>')
+ options = self.render_options(choices, [value])
+ if options:
+ output.append(options)
+ output.append('</select>')
return mark_safe(u'\n'.join(output))
+ def render_options(self, choices, selected_choices):
+ def render_option(option_value, option_label):
+ option_value = force_unicode(option_value)
+ selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
+ return u'<option value="%s"%s>%s</option>' % (
+ escape(option_value), selected_html,
+ conditional_escape(force_unicode(option_label)))
+ # Normalize to strings.
+ selected_choices = set([force_unicode(v) for v in selected_choices])
+ output = []
+ for option_value, option_label in chain(self.choices, choices):
+ if isinstance(option_label, (list, tuple)):
+ output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
+ for option in option_label:
+ output.append(render_option(*option))
+ output.append(u'</optgroup>')
+ else:
+ output.append(render_option(option_value, option_label))
+ return u'\n'.join(output)
+
class NullBooleanSelect(Select):
"""
A Select Widget intended to be used with NullBooleanField.
@@ -380,24 +395,15 @@ class NullBooleanSelect(Select):
# same thing as False.
return bool(initial) != bool(data)
-class SelectMultiple(Widget):
- def __init__(self, attrs=None, choices=()):
- super(SelectMultiple, self).__init__(attrs)
- # choices can be any iterable
- self.choices = choices
-
+class SelectMultiple(Select):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
- str_values = set([force_unicode(v) for v in value]) # Normalize to strings.
- for option_value, option_label in chain(self.choices, choices):
- option_value = force_unicode(option_value)
- selected_html = (option_value in str_values) and ' selected="selected"' or ''
- output.append(u'<option value="%s"%s>%s</option>' % (
- escape(option_value), selected_html,
- conditional_escape(force_unicode(option_label))))
- output.append(u'</select>')
+ options = self.render_options(choices, value)
+ if options:
+ output.append(options)
+ output.append('</select>')
return mark_safe(u'\n'.join(output))
def value_from_datadict(self, data, files, name):