Loading django/forms/fields.py +29 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ try: except NameError: from sets import Set as set import django.core.exceptions from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode, smart_str Loading @@ -39,6 +40,7 @@ __all__ = ( 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 'TypedChoiceField' ) # These values, if given to to_python(), will trigger the self.required check. Loading Loading @@ -657,6 +659,33 @@ class ChoiceField(Field): return True return False class TypedChoiceField(ChoiceField): def __init__(self, *args, **kwargs): self.coerce = kwargs.pop('coerce', lambda val: val) self.empty_value = kwargs.pop('empty_value', '') super(TypedChoiceField, self).__init__(*args, **kwargs) def clean(self, value): """ Validate that the value is in self.choices and can be coerced to the right type. """ value = super(TypedChoiceField, self).clean(value) if value == self.empty_value or value in EMPTY_VALUES: return self.empty_value # Hack alert: This field is purpose-made to use with Field.to_python as # a coercion function so that ModelForms with choices work. However, # Django's Field.to_python raises django.core.exceptions.ValidationError, # which is a *different* exception than # django.forms.utils.ValidationError. So unfortunatly we need to catch # both. try: value = self.coerce(value) except (ValueError, TypeError, django.core.exceptions.ValidationError): raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) return value class MultipleChoiceField(ChoiceField): hidden_widget = MultipleHiddenInput widget = SelectMultiple Loading docs/ref/forms/fields.txt +27 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,33 @@ Takes one extra required argument: An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this field. ``TypedChoiceField`` ~~~~~~~~~~~~~~~~~~~~ .. class:: TypedChoiceField(**kwargs) Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes an extra ``coerce`` argument. * Default widget: ``Select`` * Empty value: Whatever you've given as ``empty_value`` * Normalizes to: the value returned by the ``coerce`` argument. * Validates that the given value exists in the list of choices. * Error message keys: ``required``, ``invalid_choice`` Takes extra arguments: .. attribute:: TypedChoiceField.coerce 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. .. attribute:: TypedChoiceField.empty_value The value to use to represent "empty." Defaults to the empty string; ``None`` is another common choice here. ``DateField`` ~~~~~~~~~~~~~ Loading tests/regressiontests/forms/fields.py +47 −0 Original line number Diff line number Diff line Loading @@ -1077,6 +1077,53 @@ Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] # TypedChoiceField ############################################################ # TypedChoiceField is just like ChoiceField, except that coerced types will # be returned: >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) >>> f.clean('1') 1 >>> f.clean('2') Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. 2 is not one of the available choices.'] # Different coercion, same validation. >>> f.coerce = float >>> f.clean('1') 1.0 # This can also cause weirdness: be careful (bool(-1) == True, remember) >>> f.coerce = bool >>> f.clean('-1') True # Even more weirdness: if you have a valid choice but your coercion function # can't coerce, you'll still get a validation error. Don't do this! >>> f = TypedChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) >>> f.clean('B') Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. B is not one of the available choices.'] # Required fields require values >>> f.clean('') Traceback (most recent call last): ... ValidationError: [u'This field is required.'] # Non-required fields aren't required >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) >>> f.clean('') '' # If you want cleaning an empty value to return a different type, tell the field >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) >>> print f.clean('') None # NullBooleanField ############################################################ >>> f = NullBooleanField() Loading Loading
django/forms/fields.py +29 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ try: except NameError: from sets import Set as set import django.core.exceptions from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode, smart_str Loading @@ -39,6 +40,7 @@ __all__ = ( 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 'TypedChoiceField' ) # These values, if given to to_python(), will trigger the self.required check. Loading Loading @@ -657,6 +659,33 @@ class ChoiceField(Field): return True return False class TypedChoiceField(ChoiceField): def __init__(self, *args, **kwargs): self.coerce = kwargs.pop('coerce', lambda val: val) self.empty_value = kwargs.pop('empty_value', '') super(TypedChoiceField, self).__init__(*args, **kwargs) def clean(self, value): """ Validate that the value is in self.choices and can be coerced to the right type. """ value = super(TypedChoiceField, self).clean(value) if value == self.empty_value or value in EMPTY_VALUES: return self.empty_value # Hack alert: This field is purpose-made to use with Field.to_python as # a coercion function so that ModelForms with choices work. However, # Django's Field.to_python raises django.core.exceptions.ValidationError, # which is a *different* exception than # django.forms.utils.ValidationError. So unfortunatly we need to catch # both. try: value = self.coerce(value) except (ValueError, TypeError, django.core.exceptions.ValidationError): raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) return value class MultipleChoiceField(ChoiceField): hidden_widget = MultipleHiddenInput widget = SelectMultiple Loading
docs/ref/forms/fields.txt +27 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,33 @@ Takes one extra required argument: An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this field. ``TypedChoiceField`` ~~~~~~~~~~~~~~~~~~~~ .. class:: TypedChoiceField(**kwargs) Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes an extra ``coerce`` argument. * Default widget: ``Select`` * Empty value: Whatever you've given as ``empty_value`` * Normalizes to: the value returned by the ``coerce`` argument. * Validates that the given value exists in the list of choices. * Error message keys: ``required``, ``invalid_choice`` Takes extra arguments: .. attribute:: TypedChoiceField.coerce 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. .. attribute:: TypedChoiceField.empty_value The value to use to represent "empty." Defaults to the empty string; ``None`` is another common choice here. ``DateField`` ~~~~~~~~~~~~~ Loading
tests/regressiontests/forms/fields.py +47 −0 Original line number Diff line number Diff line Loading @@ -1077,6 +1077,53 @@ Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] # TypedChoiceField ############################################################ # TypedChoiceField is just like ChoiceField, except that coerced types will # be returned: >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) >>> f.clean('1') 1 >>> f.clean('2') Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. 2 is not one of the available choices.'] # Different coercion, same validation. >>> f.coerce = float >>> f.clean('1') 1.0 # This can also cause weirdness: be careful (bool(-1) == True, remember) >>> f.coerce = bool >>> f.clean('-1') True # Even more weirdness: if you have a valid choice but your coercion function # can't coerce, you'll still get a validation error. Don't do this! >>> f = TypedChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) >>> f.clean('B') Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. B is not one of the available choices.'] # Required fields require values >>> f.clean('') Traceback (most recent call last): ... ValidationError: [u'This field is required.'] # Non-required fields aren't required >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) >>> f.clean('') '' # If you want cleaning an empty value to return a different type, tell the field >>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) >>> print f.clean('') None # NullBooleanField ############################################################ >>> f = NullBooleanField() Loading