Ticket #12398: 12398-TypedMultipleChoiceField-r14586.diff
File 12398-TypedMultipleChoiceField-r14586.diff, 7.1 KB (added by , 14 years ago) |
---|
-
django/forms/fields.py
40 40 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 41 41 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 42 42 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 43 'TypedChoiceField' 43 'TypedChoiceField', 'TypedMultipleChoiceField' 44 44 ) 45 45 46 46 def en_format(name): … … 691 691 692 692 def to_python(self, value): 693 693 """ 694 Validate that the value is in self.choices and can be coerced to the694 Validates that the value is in self.choices and can be coerced to the 695 695 right type. 696 696 """ 697 697 value = super(TypedChoiceField, self).to_python(value) … … 733 733 if not self.valid_value(val): 734 734 raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) 735 735 736 class TypedMultipleChoiceField(MultipleChoiceField): 737 def __init__(self, *args, **kwargs): 738 self.coerce = kwargs.pop('coerce', lambda val: val) 739 self.empty_value = kwargs.pop('empty_value', []) 740 super(TypedMultipleChoiceField, self).__init__(*args, **kwargs) 741 742 def to_python(self, value): 743 """ 744 Validates that the values are in self.choices and can be coerced to the 745 right type. 746 """ 747 value = super(TypedMultipleChoiceField, self).to_python(value) 748 super(TypedMultipleChoiceField, self).validate(value) 749 if value == self.empty_value or value in validators.EMPTY_VALUES: 750 return self.empty_value 751 new_value = [] 752 for choice in value: 753 try: 754 new_value.append(self.coerce(choice)) 755 except (ValueError, TypeError, ValidationError): 756 raise ValidationError(self.error_messages['invalid_choice'] % {'value': choice}) 757 return new_value 758 759 def validate(self, value): 760 pass 761 736 762 class ComboField(Field): 737 763 """ 738 764 A Field whose clean() method calls multiple Field clean() methods. -
tests/regressiontests/forms/tests/fields.py
749 749 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 6 is not one of the available choices.']", f.clean, ['6']) 750 750 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 6 is not one of the available choices.']", f.clean, ['1','6']) 751 751 752 # ComboField ################################################################## 752 # TypedMultipleChoiceField ############################################################ 753 # TypedMultipleChoiceField is just like MultipleChoiceField, except that coerced types 754 # will be returned: 753 755 756 def test_typedmultiplechoicefield_1(self): 757 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) 758 self.assertEqual([1], f.clean(['1'])) 759 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 2 is not one of the available choices.']", f.clean, ['2']) 760 761 def test_typedmultiplechoicefield_2(self): 762 # Different coercion, same validation. 763 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=float) 764 self.assertEqual([1.0], f.clean(['1'])) 765 766 def test_typedmultiplechoicefield_3(self): 767 # This can also cause weirdness: be careful (bool(-1) == True, remember) 768 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=bool) 769 self.assertEqual([True], f.clean(['-1'])) 770 771 def test_typedmultiplechoicefield_4(self): 772 # Even more weirdness: if you have a valid choice but your coercion function 773 # can't coerce, you'll still get a validation error. Don't do this! 774 f = TypedMultipleChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) 775 self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. B is not one of the available choices.']", f.clean, ['B']) 776 # Required fields require values 777 self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, []) 778 779 def test_typedmultiplechoicefield_5(self): 780 # Non-required fields aren't required 781 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) 782 self.assertEqual([], f.clean([])) 783 784 def test_typedmultiplechoicefield_6(self): 785 # If you want cleaning an empty value to return a different type, tell the field 786 f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) 787 self.assertEqual(None, f.clean([])) 788 789 # ComboField ################################################################## 790 754 791 def test_combofield_1(self): 755 792 f = ComboField(fields=[CharField(max_length=20), EmailField()]) 756 793 self.assertEqual(u'test@example.com', f.clean('test@example.com')) -
docs/ref/forms/fields.txt
361 361 362 362 .. class:: TypedChoiceField(**kwargs) 363 363 364 Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes an365 extra ``coerce`` argument.364 Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes two 365 extra arguments, ``coerce`` and ``empty_value``. 366 366 367 367 * Default widget: ``Select`` 368 368 * Empty value: Whatever you've given as ``empty_value`` 369 * Normalizes to: the value returned by the ``coerce`` argument. 370 * Validates that the given value exists in the list of choices. 369 * Normalizes to: The value returned by the ``coerce`` argument. 370 * Validates that the given value exists in the list of choices and can be 371 coerced. 371 372 * Error message keys: ``required``, ``invalid_choice`` 372 373 373 374 Takes extra arguments: … … 635 636 of choices. 636 637 * Error message keys: ``required``, ``invalid_choice``, ``invalid_list`` 637 638 638 Takes one extra argument, ``choices``, as for ``ChoiceField``.639 Takes one extra required argument, ``choices``, as for ``ChoiceField``. 639 640 641 ``TypedMultipleChoiceField`` 642 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 643 644 .. class:: TypedMultipleChoiceField(**kwargs) 645 646 Just like a :class:`MultipleChoiceField`, except :class:`TypedMultipleChoiceField` 647 takes two extra arguments, ``coerce`` and ``empty_value``. 648 649 * Default widget: ``SelectMultiple`` 650 * Empty value: Whatever you've given as ``empty_value`` 651 * Normalizes to: A list of values, each returned by the ``coerce`` argument. 652 * Validates that the given values exists in the list of choices and can be 653 coerced. 654 * Error message keys: ``required``, ``invalid_choice`` 655 656 Takes two extra arguments, ``coerce`` and ``empty_value``, as for ``TypedChoiceField``. 657 640 658 ``NullBooleanField`` 641 659 ~~~~~~~~~~~~~~~~~~~~ 642 660