Opened 2 years ago
Closed 2 years ago
#34156 closed Bug (invalid)
TypedChoiceField not compatible with IntegerChoices
Reported by: | Yoshio Hasegawa | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 4.1 |
Severity: | Normal | Keywords: | Form, TypedChoiceField, IntegerChoices, Coercion |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
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
class SomeIntegerChoice(models.IntegerChoices): VAL_2350 = (2350, "value 2350") VAL_4100 = (4100, "value 4100") VAL_8760 = (8760, "value 8760")
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:
# 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:
# 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)
As far as I'm aware, it's an issue in your code. For
IntegerChoices
you should passcoerce=int
and everything works fine.