mirror of https://github.com/django/django.git
Fixed #21397 -- Re-added flexibility to TypedChoiceField coercion
Thanks Elec for the report and Simon Charette for the review.
This commit is contained in:
parent
4a00f132e0
commit
a0f3eeccf3
|
@ -822,12 +822,10 @@ class TypedChoiceField(ChoiceField):
|
|||
self.empty_value = kwargs.pop('empty_value', '')
|
||||
super(TypedChoiceField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
def _coerce(self, value):
|
||||
"""
|
||||
Validates that the value is in self.choices and can be coerced to the
|
||||
right type.
|
||||
Validate that the value can be coerced to the right type (if not empty).
|
||||
"""
|
||||
value = super(TypedChoiceField, self).to_python(value)
|
||||
if value == self.empty_value or value in self.empty_values:
|
||||
return self.empty_value
|
||||
try:
|
||||
|
@ -840,6 +838,10 @@ class TypedChoiceField(ChoiceField):
|
|||
)
|
||||
return value
|
||||
|
||||
def clean(self, value):
|
||||
value = super(TypedChoiceField, self).clean(value)
|
||||
return self._coerce(value)
|
||||
|
||||
|
||||
class MultipleChoiceField(ChoiceField):
|
||||
hidden_widget = MultipleHiddenInput
|
||||
|
@ -889,12 +891,11 @@ class TypedMultipleChoiceField(MultipleChoiceField):
|
|||
self.empty_value = kwargs.pop('empty_value', [])
|
||||
super(TypedMultipleChoiceField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
def _coerce(self, value):
|
||||
"""
|
||||
Validates that the values are in self.choices and can be coerced to the
|
||||
right type.
|
||||
"""
|
||||
value = super(TypedMultipleChoiceField, self).to_python(value)
|
||||
if value == self.empty_value or value in self.empty_values:
|
||||
return self.empty_value
|
||||
new_value = []
|
||||
|
@ -909,6 +910,10 @@ class TypedMultipleChoiceField(MultipleChoiceField):
|
|||
)
|
||||
return new_value
|
||||
|
||||
def clean(self, value):
|
||||
value = super(TypedMultipleChoiceField, self).clean(value)
|
||||
return self._coerce(value)
|
||||
|
||||
def validate(self, value):
|
||||
if value != self.empty_value:
|
||||
super(TypedMultipleChoiceField, self).validate(value)
|
||||
|
|
|
@ -375,7 +375,9 @@ For each field, we describe the default widget used if you don't specify
|
|||
|
||||
A function that takes one argument and returns a coerced value. Examples
|
||||
include the built-in ``int``, ``float``, ``bool`` and other types. Defaults
|
||||
to an identity function.
|
||||
to an identity function. Note that coercion happens after input
|
||||
validation, so it is possible to coerce to a value not present in
|
||||
``choices``.
|
||||
|
||||
.. attribute:: empty_value
|
||||
|
||||
|
|
|
@ -317,6 +317,10 @@ Forms
|
|||
return ``self.cleaned_data``. If it does return a changed dictionary then
|
||||
that will still be used.
|
||||
|
||||
* After a temporary regression in Django 1.6, it's now possible again to make
|
||||
:class:`~django.forms.TypedChoiceField` ``coerce`` method return an arbitrary
|
||||
value.
|
||||
|
||||
* :attr:`SelectDateWidget.months
|
||||
<django.forms.extras.widgets.SelectDateWidget.months>` can be used to
|
||||
customize the wording of the months displayed in the select widget.
|
||||
|
|
|
@ -956,6 +956,22 @@ class FieldsTests(SimpleTestCase):
|
|||
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||
self.assertFalse(f._has_changed(None, ''))
|
||||
|
||||
def test_typedchoicefield_special_coerce(self):
|
||||
"""
|
||||
Test a coerce function which results in a value not present in choices.
|
||||
Refs #21397.
|
||||
"""
|
||||
def coerce_func(val):
|
||||
return Decimal('1.%s' % val)
|
||||
|
||||
f = TypedChoiceField(choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True)
|
||||
self.assertEqual(Decimal('1.2'), f.clean('2'))
|
||||
self.assertRaisesMessage(ValidationError,
|
||||
"'This field is required.'", f.clean, '')
|
||||
self.assertRaisesMessage(ValidationError,
|
||||
"'Select a valid choice. 3 is not one of the available choices.'",
|
||||
f.clean, '3')
|
||||
|
||||
# NullBooleanField ############################################################
|
||||
|
||||
def test_nullbooleanfield_1(self):
|
||||
|
@ -1110,6 +1126,23 @@ class FieldsTests(SimpleTestCase):
|
|||
f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||
self.assertFalse(f._has_changed(None, ''))
|
||||
|
||||
def test_typedmultiplechoicefield_special_coerce(self):
|
||||
"""
|
||||
Test a coerce function which results in a value not present in choices.
|
||||
Refs #21397.
|
||||
"""
|
||||
def coerce_func(val):
|
||||
return Decimal('1.%s' % val)
|
||||
|
||||
f = TypedMultipleChoiceField(
|
||||
choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True)
|
||||
self.assertEqual([Decimal('1.2')], f.clean(['2']))
|
||||
self.assertRaisesMessage(ValidationError,
|
||||
"'This field is required.'", f.clean, [])
|
||||
self.assertRaisesMessage(ValidationError,
|
||||
"'Select a valid choice. 3 is not one of the available choices.'",
|
||||
f.clean, ['3'])
|
||||
|
||||
# ComboField ##################################################################
|
||||
|
||||
def test_combofield_1(self):
|
||||
|
|
Loading…
Reference in New Issue