Ticket #21397: 21397-1.diff

File 21397-1.diff, 4.9 KB (added by Claude Paroz, 10 years ago)
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index c94a564..c6c6ca5 100644
    a b class TypedChoiceField(ChoiceField):  
    812812        self.empty_value = kwargs.pop('empty_value', '')
    813813        super(TypedChoiceField, self).__init__(*args, **kwargs)
    814814
    815     def to_python(self, value):
     815    def _coerce(self, value):
    816816        """
    817         Validates that the value is in self.choices and can be coerced to the
    818         right type.
     817        Validate that the value can be coerced to the right type (if not empty).
    819818        """
    820         value = super(TypedChoiceField, self).to_python(value)
    821819        if value == self.empty_value or value in self.empty_values:
    822820            return self.empty_value
    823821        try:
    class TypedChoiceField(ChoiceField):  
    830828            )
    831829        return value
    832830
     831    def clean(self, value):
     832        value = super(TypedChoiceField, self).clean(value)
     833        return self._coerce(value)
     834
    833835
    834836class MultipleChoiceField(ChoiceField):
    835837    hidden_widget = MultipleHiddenInput
    class TypedMultipleChoiceField(MultipleChoiceField):  
    879881        self.empty_value = kwargs.pop('empty_value', [])
    880882        super(TypedMultipleChoiceField, self).__init__(*args, **kwargs)
    881883
    882     def to_python(self, value):
     884    def _coerce(self, value):
    883885        """
    884886        Validates that the values are in self.choices and can be coerced to the
    885887        right type.
    886888        """
    887         value = super(TypedMultipleChoiceField, self).to_python(value)
    888889        if value == self.empty_value or value in self.empty_values:
    889890            return self.empty_value
    890891        new_value = []
    class TypedMultipleChoiceField(MultipleChoiceField):  
    899900                )
    900901        return new_value
    901902
     903    def clean(self, value):
     904        value = super(TypedMultipleChoiceField, self).clean(value)
     905        return self._coerce(value)
     906
    902907    def validate(self, value):
    903908        if value != self.empty_value:
    904909            super(TypedMultipleChoiceField, self).validate(value)
  • docs/ref/forms/fields.txt

    diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
    index ef4ed72..476149b 100644
    a b For each field, we describe the default widget used if you don't specify  
    375375
    376376        A function that takes one argument and returns a coerced value. Examples
    377377        include the built-in ``int``, ``float``, ``bool`` and other types. Defaults
    378         to an identity function.
     378        to an identity function. Note that coercion happens after input
     379        validation, so it is possible to coerce to a value not present in
     380        ``choices``.
    379381
    380382    .. attribute:: empty_value
    381383
  • tests/forms_tests/tests/test_fields.py

    diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py
    index f02593e..0b1826b 100644
    a b class FieldsTests(SimpleTestCase):  
    950950        f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
    951951        self.assertFalse(f._has_changed(None, ''))
    952952
     953    def test_typedchoicefield_special_coerce(self):
     954        """
     955        Test a coerce function which results in a value not present in choices.
     956        Refs #21397.
     957        """
     958        def coerce_func(val):
     959            return Decimal('1.%s' % val)
     960
     961        f = TypedChoiceField(choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True)
     962        self.assertEqual(Decimal('1.2'), f.clean('2'))
     963        self.assertRaisesMessage(ValidationError,
     964            "'This field is required.'", f.clean, '')
     965        self.assertRaisesMessage(ValidationError,
     966            "'Select a valid choice. 3 is not one of the available choices.'",
     967            f.clean, '3')
     968
    953969    # NullBooleanField ############################################################
    954970
    955971    def test_nullbooleanfield_1(self):
    class FieldsTests(SimpleTestCase):  
    11041120        f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
    11051121        self.assertFalse(f._has_changed(None, ''))
    11061122
     1123    def test_typedmultiplechoicefield_special_coerce(self):
     1124        """
     1125        Test a coerce function which results in a value not present in choices.
     1126        Refs #21397.
     1127        """
     1128        def coerce_func(val):
     1129            return Decimal('1.%s' % val)
     1130
     1131        f = TypedMultipleChoiceField(
     1132            choices=[(1, "1"), (2, "2")], coerce=coerce_func, required=True)
     1133        self.assertEqual([Decimal('1.2')], f.clean(['2']))
     1134        self.assertRaisesMessage(ValidationError,
     1135            "'This field is required.'", f.clean, [])
     1136        self.assertRaisesMessage(ValidationError,
     1137            "'Select a valid choice. 3 is not one of the available choices.'",
     1138            f.clean, ['3'])
     1139
    11071140   # ComboField ##################################################################
    11081141
    11091142    def test_combofield_1(self):
Back to Top