From fbb42c31df691e3f83236ec8d3a25318ab759e87 Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Tue, 2 Feb 2010 00:41:28 +0000 Subject: [PATCH] Fixed #12667. Added optgroup validation support to model fields with choices. Thanks ramiro. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12374 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/fields/__init__.py | 16 ++++++++++------ tests/regressiontests/model_fields/tests.py | 10 +++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 29de8c7422..4a3405f3f0 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -1,16 +1,13 @@ import datetime import decimal -import os import re import time import django.utils.copycompat as copy from django.db import connection -from django.db.models import signals from django.db.models.fields.subclassing import LegacyConnection from django.db.models.query_utils import QueryWrapper -from django.dispatch import dispatcher from django.conf import settings from django import forms from django.core import exceptions, validators @@ -18,7 +15,7 @@ from django.utils.datastructures import DictWrapper from django.utils.functional import curry from django.utils.itercompat import tee from django.utils.text import capfirst -from django.utils.translation import ugettext_lazy as _, ugettext +from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode, force_unicode, smart_str from django.utils import datetime_safe @@ -198,8 +195,15 @@ class Field(object): # Skip validation for non-editable fields. return if self._choices and value: - if not value in dict(self.choices): - raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value) + for option_key, option_value in self.choices: + if type(option_value) in (tuple, list): + # This is an optgroup, so look inside the group for options. + for optgroup_key, optgroup_value in option_value: + if value == optgroup_key: + return + elif value == option_key: + return + raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value) if value is None and not self.null: raise exceptions.ValidationError(self.error_messages['null']) diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index dd4e7462bf..18bfbd37ee 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -173,6 +173,10 @@ class ValidationTest(django.test.TestCase): f = models.CharField(choices=[('a','A'), ('b','B')]) self.assertRaises(ValidationError, f.clean, "not a", None) + def test_choices_validation_supports_named_groups(self): + f = models.IntegerField(choices=(('group',((10,'A'),(20,'B'))),(30,'C'))) + self.assertEqual(10, f.clean(10, None)) + def test_nullable_integerfield_raises_error_with_blank_false(self): f = models.IntegerField(null=True, blank=False) self.assertRaises(ValidationError, f.clean, None, None) @@ -202,7 +206,7 @@ class ValidationTest(django.test.TestCase): class BigIntegerFieldTests(django.test.TestCase): def test_limits(self): # Ensure that values that are right at the limits can be saved - # and then retrieved without corruption. + # and then retrieved without corruption. maxval = 9223372036854775807 minval = -maxval - 1 BigInt.objects.create(value=maxval) @@ -236,7 +240,7 @@ class TypeCoercionTests(django.test.TestCase): """ def test_lookup_integer_in_charfield(self): self.assertEquals(Post.objects.filter(title=9).count(), 0) - + def test_lookup_integer_in_textfield(self): self.assertEquals(Post.objects.filter(body=24).count(), 0) - +