Fixed #28748 -- Made model field choices check more strict for named groups.
This commit is contained in:
parent
8cdeb8acfc
commit
f9844f4841
|
@ -241,31 +241,55 @@ class Field(RegisterLookupMixin):
|
|||
return []
|
||||
|
||||
def _check_choices(self):
|
||||
if self.choices:
|
||||
if isinstance(self.choices, str) or not is_iterable(self.choices):
|
||||
return [
|
||||
checks.Error(
|
||||
"'choices' must be an iterable (e.g., a list or tuple).",
|
||||
obj=self,
|
||||
id='fields.E004',
|
||||
)
|
||||
]
|
||||
elif any(isinstance(choice, str) or
|
||||
not is_iterable(choice) or len(choice) != 2
|
||||
for choice in self.choices):
|
||||
return [
|
||||
checks.Error(
|
||||
"'choices' must be an iterable containing "
|
||||
"(actual value, human readable name) tuples.",
|
||||
obj=self,
|
||||
id='fields.E005',
|
||||
)
|
||||
]
|
||||
else:
|
||||
return []
|
||||
if not self.choices:
|
||||
return []
|
||||
|
||||
def is_value(value):
|
||||
return isinstance(value, str) or not is_iterable(value)
|
||||
|
||||
if is_value(self.choices):
|
||||
return [
|
||||
checks.Error(
|
||||
"'choices' must be an iterable (e.g., a list or tuple).",
|
||||
obj=self,
|
||||
id='fields.E004',
|
||||
)
|
||||
]
|
||||
|
||||
# Expect [group_name, [value, display]]
|
||||
for choices_group in self.choices:
|
||||
try:
|
||||
group_name, group_choices = choices_group
|
||||
except ValueError:
|
||||
# Containing non-pairs
|
||||
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:
|
||||
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):
|
||||
if self.db_index not in (None, True, False):
|
||||
return [
|
||||
|
|
|
@ -210,6 +210,44 @@ class CharFieldTests(TestCase):
|
|||
|
||||
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):
|
||||
class Model(models.Model):
|
||||
field = models.CharField(max_length=10, db_index='bad')
|
||||
|
|
Loading…
Reference in New Issue