Fixed #30757 -- Added a system check to ensure max_length fits the longest choice.
This commit is contained in:
parent
fee75d2aed
commit
b6251956b6
|
@ -257,6 +257,7 @@ class Field(RegisterLookupMixin):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
choice_max_length = 0
|
||||||
# Expect [group_name, [value, display]]
|
# Expect [group_name, [value, display]]
|
||||||
for choices_group in self.choices:
|
for choices_group in self.choices:
|
||||||
try:
|
try:
|
||||||
|
@ -270,16 +271,32 @@ class Field(RegisterLookupMixin):
|
||||||
for value, human_name in group_choices
|
for value, human_name in group_choices
|
||||||
):
|
):
|
||||||
break
|
break
|
||||||
|
if self.max_length is not None and group_choices:
|
||||||
|
choice_max_length = max(
|
||||||
|
choice_max_length,
|
||||||
|
*(len(value) for value, _ in group_choices if isinstance(value, str)),
|
||||||
|
)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
# No groups, choices in the form [value, display]
|
# No groups, choices in the form [value, display]
|
||||||
value, human_name = group_name, group_choices
|
value, human_name = group_name, group_choices
|
||||||
if not is_value(value) or not is_value(human_name):
|
if not is_value(value) or not is_value(human_name):
|
||||||
break
|
break
|
||||||
|
if self.max_length is not None and isinstance(value, str):
|
||||||
|
choice_max_length = max(choice_max_length, len(value))
|
||||||
|
|
||||||
# Special case: choices=['ab']
|
# Special case: choices=['ab']
|
||||||
if isinstance(choices_group, str):
|
if isinstance(choices_group, str):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
if self.max_length is not None and choice_max_length > self.max_length:
|
||||||
|
return [
|
||||||
|
checks.Error(
|
||||||
|
"'max_length' is too small to fit the longest value "
|
||||||
|
"in 'choices' (%d characters)." % choice_max_length,
|
||||||
|
obj=self,
|
||||||
|
id='fields.E009',
|
||||||
|
),
|
||||||
|
]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -138,6 +138,8 @@ Model fields
|
||||||
* **fields.E006**: ``db_index`` must be ``None``, ``True`` or ``False``.
|
* **fields.E006**: ``db_index`` must be ``None``, ``True`` or ``False``.
|
||||||
* **fields.E007**: Primary keys must not have ``null=True``.
|
* **fields.E007**: Primary keys must not have ``null=True``.
|
||||||
* **fields.E008**: All ``validators`` must be callable.
|
* **fields.E008**: All ``validators`` must be callable.
|
||||||
|
* **fields.E009**: ``max_length`` is too small to fit the longest value in
|
||||||
|
``choices`` (``<count>`` characters).
|
||||||
* **fields.E100**: ``AutoField``\s must set primary_key=True.
|
* **fields.E100**: ``AutoField``\s must set primary_key=True.
|
||||||
* **fields.E110**: ``BooleanField``\s do not accept null values. *This check
|
* **fields.E110**: ``BooleanField``\s do not accept null values. *This check
|
||||||
appeared before support for null values was added in Django 2.1.*
|
appeared before support for null values was added in Django 2.1.*
|
||||||
|
|
|
@ -304,6 +304,32 @@ class CharFieldTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(Model._meta.get_field('field').check(), [])
|
self.assertEqual(Model._meta.get_field('field').check(), [])
|
||||||
|
|
||||||
|
def test_choices_in_max_length(self):
|
||||||
|
class Model(models.Model):
|
||||||
|
field = models.CharField(
|
||||||
|
max_length=2, choices=[
|
||||||
|
('ABC', 'Value Too Long!'), ('OK', 'Good')
|
||||||
|
],
|
||||||
|
)
|
||||||
|
group = models.CharField(
|
||||||
|
max_length=2, choices=[
|
||||||
|
('Nested', [('OK', 'Good'), ('Longer', 'Longer')]),
|
||||||
|
('Grouped', [('Bad', 'Bad')]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
for name, choice_max_length in (('field', 3), ('group', 6)):
|
||||||
|
with self.subTest(name):
|
||||||
|
field = Model._meta.get_field(name)
|
||||||
|
self.assertEqual(field.check(), [
|
||||||
|
Error(
|
||||||
|
"'max_length' is too small to fit the longest value "
|
||||||
|
"in 'choices' (%d characters)." % choice_max_length,
|
||||||
|
obj=field,
|
||||||
|
id='fields.E009',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
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