Opened 3 weeks ago

Last modified 11 days ago

#28748 new Bug

Named groups in choices are not properly validated

Reported by: Scott Stevens Owned by:
Component: Database layer (models, ORM) Version: 2.0
Severity: Normal Keywords: choices
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When using named groups for the choices attribute, I accidentally created them incorrectly, like so:

tournament = models.PositiveSmallIntegerField(db_index=True, choices=(
    (4, 'g'),
    ('NamedTuple', (5, 'h'), ),
))

Given that specifying choices as (4, 'g'), ('NamedTuple', (5, 'h'), (6, 'i'), ), raises E.005 (as does 1, 2, 3,), I would expect the same result for these malformed named groups.

Instead, it allows ./manage.py makemigrations to run successfully (the check is not performed when launching ./manage.py shell, which might be its own issue). However, it raises this exception when performing a full_clean() on a model and thus validating the field in question (assuming the field has been set):

  File "...\django\db\models\base.py", line 1144, in full_clean
    self.clean_fields(exclude=exclude)
  File ...\django\db\models\base.py", line 1186, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
  File "...\django\db\models\fields\__init__.py", line 607, in clean
    self.validate(value, model_instance)
  File "...\django\db\models\fields\__init__.py", line 583, in validate
    for optgroup_key, optgroup_value in option_value:
TypeError: 'int' object is not iterable

The flatchoices attribute ends up as [5, 'h', (4, 'g')].

It is my understanding that this behaviour is unintended, as the _check_choices() method performs validation on the choices attribute.

Specifically, this clause

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',
    )
  ]

Does not check beyond the length of each choice, even if that "choice" is a named group.

This can be further seen with this set of choices:

tournament = models.PositiveSmallIntegerField(db_index=True, choices=(
  (4, 'g'),
  ('NamedTuple', (
    (5, 'h'),
    (2, 3, 4, ),
  )),
))

Migrations are again created successfully, but when validating the field, it raises
ValueError: too many values to unpack (expected 2)
on the same line as before.

Change History (3)

comment:1 Changed 3 weeks ago by Tim Graham

Triage Stage: UnreviewedAccepted

comment:2 Changed 3 weeks ago by Aaron Kirkbride

Owner: changed from nobody to Aaron Kirkbride
Status: newassigned

comment:3 Changed 11 days ago by Aaron Kirkbride

Owner: Aaron Kirkbride deleted
Status: assignednew
Note: See TracTickets for help on using tickets.
Back to Top