#28748: Named groups in choices are not properly validated
-------------------------------------+-------------------------------------
               Reporter:  Scott      |          Owner:  nobody
  Stevens                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  2.0
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:  choices
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 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.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/28748>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/052.d5c6e029ea9871501d702df7fcaea507%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to