Fixed #28748 -- Made model field choices check more strict for named groups.

This commit is contained in:
François Freitag 2017-11-29 22:33:24 -08:00 committed by Tim Graham
parent 8cdeb8acfc
commit f9844f4841
2 changed files with 84 additions and 22 deletions

View File

@ -241,31 +241,55 @@ class Field(RegisterLookupMixin):
return [] return []
def _check_choices(self): def _check_choices(self):
if self.choices: if not self.choices:
if isinstance(self.choices, str) or not is_iterable(self.choices): return []
return [
checks.Error( def is_value(value):
"'choices' must be an iterable (e.g., a list or tuple).", return isinstance(value, str) or not is_iterable(value)
obj=self,
id='fields.E004', if is_value(self.choices):
) return [
] checks.Error(
elif any(isinstance(choice, str) or "'choices' must be an iterable (e.g., a list or tuple).",
not is_iterable(choice) or len(choice) != 2 obj=self,
for choice in self.choices): id='fields.E004',
return [ )
checks.Error( ]
"'choices' must be an iterable containing "
"(actual value, human readable name) tuples.", # Expect [group_name, [value, display]]
obj=self, for choices_group in self.choices:
id='fields.E005', try:
) group_name, group_choices = choices_group
] except ValueError:
else: # Containing non-pairs
return [] break
try:
if not all(
is_value(value) and is_value(human_name)
for value, human_name in group_choices
):
break
except (TypeError, ValueError):
# No groups, choices in the form [value, display]
value, human_name = group_name, group_choices
if not is_value(value) or not is_value(human_name):
break
# Special case: choices=['ab']
if isinstance(choices_group, str):
break
else: else:
return [] return []
return [
checks.Error(
"'choices' must be an iterable containing "
"(actual value, human readable name) tuples.",
obj=self,
id='fields.E005',
)
]
def _check_db_index(self): def _check_db_index(self):
if self.db_index not in (None, True, False): if self.db_index not in (None, True, False):
return [ return [

View File

@ -210,6 +210,44 @@ class CharFieldTests(TestCase):
self.assertEqual(Model._meta.get_field('field').check(), []) self.assertEqual(Model._meta.get_field('field').check(), [])
def test_choices_named_group_non_pairs(self):
class Model(models.Model):
field = models.CharField(
max_length=10,
choices=[['knights', [['L', 'Lancelot', 'Du Lac']]]],
)
field = Model._meta.get_field('field')
self.assertEqual(field.check(), [
Error(
"'choices' must be an iterable containing (actual value, "
"human readable name) tuples.",
obj=field,
id='fields.E005',
),
])
def test_choices_named_group_bad_structure(self):
class Model(models.Model):
field = models.CharField(
max_length=10, choices=[
['knights', [
['Noble', [['G', 'Galahad']]],
['Combative', [['L', 'Lancelot']]],
]],
],
)
field = Model._meta.get_field('field')
self.assertEqual(field.check(), [
Error(
"'choices' must be an iterable containing (actual value, "
"human readable name) tuples.",
obj=field,
id='fields.E005',
),
])
def test_bad_db_index_value(self): def test_bad_db_index_value(self):
class Model(models.Model): class Model(models.Model):
field = models.CharField(max_length=10, db_index='bad') field = models.CharField(max_length=10, db_index='bad')