Django

Code

Changeset 8771

Show
Ignore:
Timestamp:
08/31/08 15:10:50 (3 months ago)
Author:
jacob
Message:

Added a TypedChoiceField which acts just like ChoiceField, except that it
returns a value coerced by some provided function. Refs #6967.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/forms/fields.py

    r8661 r8771  
    2424    from sets import Set as set 
    2525 
     26import django.core.exceptions 
    2627from django.utils.translation import ugettext_lazy as _ 
    2728from django.utils.encoding import smart_unicode, smart_str 
     
    4041    'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 
    4142    'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'SlugField', 
     43    'TypedChoiceField' 
    4244) 
    4345 
     
    657659                    return True 
    658660        return False 
     661 
     662class TypedChoiceField(ChoiceField): 
     663    def __init__(self, *args, **kwargs): 
     664        self.coerce = kwargs.pop('coerce', lambda val: val) 
     665        self.empty_value = kwargs.pop('empty_value', '') 
     666        super(TypedChoiceField, self).__init__(*args, **kwargs) 
     667         
     668    def clean(self, value): 
     669        """ 
     670        Validate that the value is in self.choices and can be coerced to the 
     671        right type. 
     672        """ 
     673        value = super(TypedChoiceField, self).clean(value) 
     674        if value == self.empty_value or value in EMPTY_VALUES: 
     675            return self.empty_value 
     676         
     677        # Hack alert: This field is purpose-made to use with Field.to_python as 
     678        # a coercion function so that ModelForms with choices work. However, 
     679        # Django's Field.to_python raises django.core.exceptions.ValidationError, 
     680        # which is a *different* exception than 
     681        # django.forms.utils.ValidationError. So unfortunatly we need to catch 
     682        # both. 
     683        try: 
     684            value = self.coerce(value) 
     685        except (ValueError, TypeError, django.core.exceptions.ValidationError): 
     686            raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) 
     687        return value 
    659688 
    660689class MultipleChoiceField(ChoiceField): 
  • django/trunk/docs/ref/forms/fields.txt

    r8616 r8771  
    363363    An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this 
    364364    field. 
     365     
     366``TypedChoiceField`` 
     367~~~~~~~~~~~~~~~~~~~~ 
     368 
     369.. class:: TypedChoiceField(**kwargs) 
     370 
     371Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes an 
     372extra ``coerce`` argument. 
     373 
     374    * Default widget: ``Select`` 
     375    * Empty value: Whatever you've given as ``empty_value`` 
     376    * Normalizes to: the value returned by the ``coerce`` argument. 
     377    * Validates that the given value exists in the list of choices. 
     378    * Error message keys: ``required``, ``invalid_choice`` 
     379 
     380Takes extra arguments: 
     381 
     382.. attribute:: TypedChoiceField.coerce 
     383 
     384    A function that takes one argument and returns a coerced value. Examples 
     385    include the built-in ``int``, ``float``, ``bool`` and other types. Defaults 
     386    to an identity function. 
     387 
     388.. attribute:: TypedChoiceField.empty_value 
     389 
     390    The value to use to represent "empty." Defaults to the empty string; 
     391    ``None`` is another common choice here. 
    365392 
    366393``DateField`` 
  • django/trunk/tests/regressiontests/forms/fields.py

    r8661 r8771  
    10781078ValidationError: [u'Select a valid choice. 6 is not one of the available choices.'] 
    10791079 
     1080# TypedChoiceField ############################################################ 
     1081 
     1082# TypedChoiceField is just like ChoiceField, except that coerced types will  
     1083# be returned: 
     1084>>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int) 
     1085>>> f.clean('1') 
     10861 
     1087>>> f.clean('2') 
     1088Traceback (most recent call last): 
     1089... 
     1090ValidationError: [u'Select a valid choice. 2 is not one of the available choices.'] 
     1091 
     1092# Different coercion, same validation. 
     1093>>> f.coerce = float 
     1094>>> f.clean('1') 
     10951.0 
     1096 
     1097 
     1098# This can also cause weirdness: be careful (bool(-1) == True, remember) 
     1099>>> f.coerce = bool 
     1100>>> f.clean('-1')  
     1101True 
     1102 
     1103# Even more weirdness: if you have a valid choice but your coercion function 
     1104# can't coerce, you'll still get a validation error. Don't do this! 
     1105>>> f = TypedChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int) 
     1106>>> f.clean('B') 
     1107Traceback (most recent call last): 
     1108... 
     1109ValidationError: [u'Select a valid choice. B is not one of the available choices.'] 
     1110 
     1111# Required fields require values 
     1112>>> f.clean('') 
     1113Traceback (most recent call last): 
     1114... 
     1115ValidationError: [u'This field is required.'] 
     1116 
     1117# Non-required fields aren't required 
     1118>>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False) 
     1119>>> f.clean('') 
     1120'' 
     1121 
     1122# If you want cleaning an empty value to return a different type, tell the field 
     1123>>> f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None) 
     1124>>> print f.clean('') 
     1125None 
     1126 
    10801127# NullBooleanField ############################################################ 
    10811128