[Django] #34156: TypedChoiceField not compatible with IntegerChoices

0 views
Skip to first unread message

Django

unread,
Nov 14, 2022, 7:58:24 AM11/14/22
to django-...@googlegroups.com
#34156: TypedChoiceField not compatible with IntegerChoices
-------------------------------------+-------------------------------------
Reporter: | Owner: nobody
yoshiohasegawa |
Type: Bug | Status: new
Component: Forms | Version: 4.1
Severity: Normal | Keywords: Form,
| TypedChoiceField, IntegerChoices,
Triage Stage: | Coercion
Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
Coercion always fails when using IntegerChoices with a TypedChoiceField in
a Django form.

When a value is cleaned in a TypedChoiceField, the inherited ChoiceField's
`to_python()` method will return the value casted as a string. This means
that when coercion with an IntegerChoices object is attempted, it will
always fail as IntegerChoices will expect an int type when instantiated.

=== Example ===
{{{#!python
class SomeIntegerChoice(models.IntegerChoices):
VAL_2350 = (2350, "value 2350")
VAL_4100 = (4100, "value 4100")
VAL_8760 = (8760, "value 8760")
}}}

{{{#!python
form.fields["Integer_choice"] = forms.TypedChoiceField(
required=required,
choices=SomeIntegerChoice.choices,
coerce=SomeIntegerChoice,
)
}}}

This field will never pass validation since `ChoiceField` (inherited by
`TypedChoiceField`) has a `to_python()` method that casts the provided
value as a string:

{{{#!python
# class ChoiceField:
# ...
def to_python(self, value):
"""Return a string."""
if value in self.empty_values:
return ""
return str(value)
}}}

To explain further... `ChoiceField.to_python()` will be called when
`TypedChoiceField` attempts to `clean()` a value. After the value is
cleaned, it is coerced using the provided class definition via the
`coerce` property.

Here is example code from Django to show how this happens:
{{{#!python
# class TypedChoiceField:
# ...
def _coerce(self, value):
"""
Validate that the value can be coerced to the right type (if not
empty).
"""
if value == self.empty_value or value in self.empty_values:
return self.empty_value
try:
value = self.coerce(value)
except (ValueError, TypeError, ValidationError):
raise ValidationError(
self.error_messages["invalid_choice"],
code="invalid_choice",
params={"value": value},
)
return value

def clean(self, value):
value = super().clean(value)
return self._coerce(value)
}}}

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

Django

unread,
Nov 14, 2022, 11:42:48 PM11/14/22
to django-...@googlegroups.com
#34156: TypedChoiceField not compatible with IntegerChoices
-------------------------------------+-------------------------------------
Reporter: Yoshio Hasegawa | Owner: nobody
Type: Bug | Status: closed
Component: Forms | Version: 4.1
Severity: Normal | Resolution: invalid
Keywords: Form, | Triage Stage:
TypedChoiceField, IntegerChoices, | Unreviewed
Coercion |

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* status: new => closed
* resolution: => invalid


Comment:

As far as I'm aware, it's an issue in your code. For `IntegerChoices` you
should pass `coerce=int` and everything works fine.

--
Ticket URL: <https://code.djangoproject.com/ticket/34156#comment:1>

Reply all
Reply to author
Forward
0 new messages