Fixed #28748 -- Made model field choices check more strict for named groups.
This commit is contained in:
parent
8cdeb8acfc
commit
f9844f4841
|
@ -241,8 +241,13 @@ 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 []
|
||||||
|
|
||||||
|
def is_value(value):
|
||||||
|
return isinstance(value, str) or not is_iterable(value)
|
||||||
|
|
||||||
|
if is_value(self.choices):
|
||||||
return [
|
return [
|
||||||
checks.Error(
|
checks.Error(
|
||||||
"'choices' must be an iterable (e.g., a list or tuple).",
|
"'choices' must be an iterable (e.g., a list or tuple).",
|
||||||
|
@ -250,9 +255,32 @@ class Field(RegisterLookupMixin):
|
||||||
id='fields.E004',
|
id='fields.E004',
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
elif any(isinstance(choice, str) or
|
|
||||||
not is_iterable(choice) or len(choice) != 2
|
# Expect [group_name, [value, display]]
|
||||||
for choice in self.choices):
|
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 [
|
return [
|
||||||
checks.Error(
|
checks.Error(
|
||||||
"'choices' must be an iterable containing "
|
"'choices' must be an iterable containing "
|
||||||
|
@ -261,10 +289,6 @@ class Field(RegisterLookupMixin):
|
||||||
id='fields.E005',
|
id='fields.E005',
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
else:
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
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):
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue