Ticket #12398: 12398-TypedMultipleChoiceField-r11901.diff
File 12398-TypedMultipleChoiceField-r11901.diff, 6.8 KB (added by , 15 years ago) |
---|
-
django/forms/fields.py
30 30 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 31 31 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 32 32 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 33 'TypedChoiceField' 33 'TypedChoiceField', 'TypedMultipleChoiceField' 34 34 ) 35 35 36 36 # These values, if given to to_python(), will trigger the self.required check. … … 711 711 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) 712 712 return new_value 713 713 714 class TypedMultipleChoiceField(MultipleChoiceField): 715 def __init__(self, *args, **kwargs): 716 self.coerce = kwargs.pop('coerce', lambda val: val) 717 self.empty_value = kwargs.pop('empty_value', []) 718 super(TypedMultipleChoiceField, self).__init__(*args, **kwargs) 719 720 def clean(self, value): 721 """ 722 Validate that the values are in self.choices and can be coerced to the 723 right type. 724 """ 725 value = super(TypedMultipleChoiceField, self).clean(value) 726 if not value: 727 return self.empty_value 728 729 # Hack alert: This field is purpose-made to use with Field.to_python as 730 # a coercion function so that ModelForms with choices work. However, 731 # Django's Field.to_python raises 732 # django.core.exceptions.ValidationError, which is a *different* 733 # exception than django.forms.util.ValidationError. So we need to catch 734 # both. 735 new_value = [] 736 for choice in value: 737 try: 738 new_value.append(self.coerce(choice)) 739 except (ValueError, TypeError, django.core.exceptions.ValidationError): 740 raise ValidationError(self.error_messages['invalid_choice'] % {'value': choice}) 741 return new_value 742 714 743 class ComboField(Field): 715 744 """ 716 745 A Field whose clean() method calls multiple Field clean() methods. -
tests/regressiontests/forms/fields.py
732 732 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 6 is not one of the available choices.']", f.clean, ['6']) 733 733 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 6 is not one of the available choices.']", f.clean, ['1','6']) 734 734 735 # TypedMultipleChoiceField ############################################################ 736 # TypedMultipleChoiceField is just like MultipleChoiceField, except that coerced types 737 # will be returned: 738 739 def test_typedmultiplechoicefield_71(self): 740 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) 741 self.assertEqual([1], f.clean(['1'])) 742 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 2 is not one of the available choices.']", f.clean, ['2']) 743 744 def test_typedmultiplechoicefield_72(self): 745 # Different coercion, same validation. 746 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=float) 747 self.assertEqual([1.0], f.clean(['1'])) 748 749 def test_typedmultiplechoicefield_73(self): 750 # This can also cause weirdness: be careful (bool(-1) == True, remember) 751 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=bool) 752 self.assertEqual([True], f.clean(['-1'])) 753 754 def test_typedmultiplechoicefield_74(self): 755 # Even more weirdness: if you have a valid choice but your coercion function 756 # can't coerce, you'll still get a validation error. Don't do this! 757 f = TypedMultipleChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) 758 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. B is not one of the available choices.']", f.clean, ['B']) 759 # Required fields require values 760 self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, []) 761 762 def test_typedmultiplechoicefield_75(self): 763 # Non-required fields aren't required 764 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) 765 self.assertEqual([], f.clean([])) 766 767 def test_typedmultiplechoicefield_76(self): 768 # If you want cleaning an empty value to return a different type, tell the field 769 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) 770 self.assertEqual(None, f.clean([])) 771 735 772 # ComboField ################################################################## 736 773 737 774 def test_combofield_63(self): -
docs/ref/forms/fields.txt
335 335 336 336 .. class:: TypedChoiceField(**kwargs) 337 337 338 Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes an339 extra ``coerce`` argument.338 Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes two 339 extra arguments, ``coerce`` and ``empty_value``. 340 340 341 341 * Default widget: ``Select`` 342 342 * Empty value: Whatever you've given as ``empty_value`` 343 * Normalizes to: the value returned by the ``coerce`` argument. 344 * Validates that the given value exists in the list of choices. 343 * Normalizes to: The value returned by the ``coerce`` argument. 344 * Validates that the given value exists in the list of choices and can be 345 coerced. 345 346 * Error message keys: ``required``, ``invalid_choice`` 346 347 347 348 Takes extra arguments: … … 604 605 of choices. 605 606 * Error message keys: ``required``, ``invalid_choice``, ``invalid_list`` 606 607 607 Takes one extra argument, ``choices``, as for ``ChoiceField``.608 Takes one extra required argument, ``choices``, as for ``ChoiceField``. 608 609 610 ``TypedMultipleChoiceField`` 611 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 612 613 .. class:: TypedMultipleChoiceField(**kwargs) 614 615 Just like a :class:`MultipleChoiceField`, except :class:`TypedMultipleChoiceField` 616 takes two extra arguments, ``coerce`` and ``empty_value``. 617 618 * Default widget: ``SelectMultiple`` 619 * Empty value: Whatever you've given as ``empty_value`` 620 * Normalizes to: A list of values, each returned by the ``coerce`` argument. 621 * Validates that the given values exists in the list of choices and can be 622 coerced. 623 * Error message keys: ``required``, ``invalid_choice`` 624 625 Takes two extra arguments, ``coerce`` and ``empty_value``, as for ``TypedChoiceField``. 626 609 627 ``NullBooleanField`` 610 628 ~~~~~~~~~~~~~~~~~~~~ 611 629