Opened 3 years ago
Closed 3 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
IntegerChoicesyou should passcoerce=intand everything works fine.