Ticket #16306: 16306.diff

File 16306.diff, 12.1 KB (added by Chris Beaven, 13 years ago)
  • django/forms/fields.py

    diff --git a/django/forms/fields.py b/django/forms/fields.py
    index 113a5aa..e12936c 100644
    a b __all__ = (  
    4040)
    4141
    4242
     43class ValidatorAttribute(object):
     44    """
     45    A descriptor used to dynamically update a field's validators based on a
     46    single value.
     47
     48    .. attribute:: create_func
     49
     50        A function used to create a validator. It should take two arguments,
     51        the object instance and a value.
     52
     53        When a value is set to this descriptor, this function is executed and
     54        the validator it returns (if any) is added to the field's
     55        :attr:`~Field.validators` list.
     56
     57        If this descriptor had previously set a validator, it will be removed
     58        from the field's ``validators`` list.
     59
     60    .. attribute:: clean_func
     61
     62        An optional function which can modify the incoming value before setting
     63        it. The function should take the same two arguments as
     64        :attr:`create_func`.
     65    """
     66
     67    def __init__(self, create_func, clean_func=None):
     68        """
     69        :param create_func: A function that creates a validator, sets
     70            :attr:`create_func`.
     71        :param clean_func: An optional function which can be used to clean the
     72            incoming  value. Sets :attr:`clean_func`.
     73        """
     74        self.create_func = create_func
     75        self.clean_func = clean_func
     76        self.value = None
     77        self.validator = None
     78
     79    def __get__(self, obj, obj_type):
     80        return self.value
     81
     82    def __set__(self, obj, value):
     83        if self.clean_func:
     84            value = self.clean_func(obj, value)
     85        self.value = value
     86        if value is None:
     87            new_validator = None
     88        else:
     89            new_validator = self.create_func(obj, value)
     90        if new_validator is self.validator:
     91            return
     92        if self.validator:
     93            try:
     94                obj.validators.remove(self.validator)
     95            except ValueError:
     96                pass
     97        if new_validator:
     98            obj.validators.append(new_validator)
     99        self.validator = new_validator
     100
    43101class Field(object):
    44102    widget = TextInput # Default widget to use when rendering this type of Field.
    45103    hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden".
    class Field(object):  
    180238
    181239class CharField(Field):
    182240    def __init__(self, max_length=None, min_length=None, *args, **kwargs):
    183         self.max_length, self.min_length = max_length, min_length
    184241        super(CharField, self).__init__(*args, **kwargs)
    185         if min_length is not None:
    186             self.validators.append(validators.MinLengthValidator(min_length))
    187         if max_length is not None:
    188             self.validators.append(validators.MaxLengthValidator(max_length))
     242        self.max_length, self.min_length = max_length, min_length
     243
     244    max_length = ValidatorAttribute(
     245        create_func=lambda self, val: validators.MaxLengthValidator(val)
     246    )
     247
     248    min_length = ValidatorAttribute(
     249        create_func=lambda self, val: validators.MinLengthValidator(val)
     250    )
    189251
    190252    def to_python(self, value):
    191253        "Returns a Unicode object."
    class IntegerField(Field):  
    206268    }
    207269
    208270    def __init__(self, max_value=None, min_value=None, *args, **kwargs):
    209         self.max_value, self.min_value = max_value, min_value
    210271        super(IntegerField, self).__init__(*args, **kwargs)
     272        self.max_value, self.min_value = max_value, min_value
     273
     274    max_value = ValidatorAttribute(
     275        create_func=lambda self, val: validators.MaxValueValidator(val)
     276    )
    211277
    212         if max_value is not None:
    213             self.validators.append(validators.MaxValueValidator(max_value))
    214         if min_value is not None:
    215             self.validators.append(validators.MinValueValidator(min_value))
     278    min_value = ValidatorAttribute(
     279        create_func=lambda self, val: validators.MinValueValidator(val)
     280    )
    216281
    217282    def to_python(self, value):
    218283        """
    class DecimalField(Field):  
    261326        'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.')
    262327    }
    263328
    264     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
     329    def __init__(self, max_value=None, min_value=None, max_digits=None,
     330                 decimal_places=None, *args, **kwargs):
     331        super(DecimalField, self).__init__(*args, **kwargs)
    265332        self.max_value, self.min_value = max_value, min_value
    266333        self.max_digits, self.decimal_places = max_digits, decimal_places
    267         Field.__init__(self, *args, **kwargs)
    268334
    269         if max_value is not None:
    270             self.validators.append(validators.MaxValueValidator(max_value))
    271         if min_value is not None:
    272             self.validators.append(validators.MinValueValidator(min_value))
     335    max_value = ValidatorAttribute(
     336        create_func=lambda self, val: validators.MaxValueValidator(val)
     337    )
     338
     339    min_value = ValidatorAttribute(
     340        create_func=lambda self, val: validators.MinValueValidator(val)
     341    )
    273342
    274343    def to_python(self, value):
    275344        """
    class RegexField(CharField):  
    439508            error_messages['invalid'] = error_message
    440509            kwargs['error_messages'] = error_messages
    441510        super(RegexField, self).__init__(max_length, min_length, *args, **kwargs)
     511        self.regex = regex
     512
     513    def clean_regex_value(self, regex):
    442514        if isinstance(regex, basestring):
    443515            regex = re.compile(regex)
    444         self.regex = regex
    445         self.validators.append(validators.RegexValidator(regex=regex))
     516        return regex
     517
     518    regex = ValidatorAttribute(
     519        create_func=lambda obj, value: validators.RegexValidator(value),
     520        clean_func=clean_regex_value
     521    )
    446522
    447523class EmailField(CharField):
    448524    default_error_messages = {
  • tests/regressiontests/forms/tests/fields.py

    diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py
    index ad8d1d9..17da030 100644
    a b class FieldsTests(TestCase):  
    109109        self.assertEqual(f.max_length, None)
    110110        self.assertEqual(f.min_length, 10)
    111111
     112    def test_charfield_validator_attributes(self):
     113        f = CharField(min_length=2, max_length=4)
     114        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 2 characters (it has 1).']", f.clean, '1')
     115        self.assertEqual(u'123', f.clean('123'))
     116        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 4 characters (it has 5).']", f.clean, '12345')
     117
     118        f.min_length = 6
     119        f.max_length = 8
     120        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 6 characters (it has 3).']", f.clean, '123')
     121        self.assertEqual(u'1234567', f.clean('1234567'))
     122        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 8 characters (it has 9).']", f.clean, '123456789')
     123
     124        f.min_length = None
     125        f.max_length = None
     126        self.assertEqual(u'1', f.clean('1'))
     127        self.assertEqual(u'123456789', f.clean('123456789'))
     128
    112129    # IntegerField ################################################################
    113130
    114131    def test_integerfield_1(self):
    class FieldsTests(TestCase):  
    180197        self.assertEqual(f.max_value, 20)
    181198        self.assertEqual(f.min_value, 10)
    182199
     200    def test_integerfield_validator_attributes(self):
     201        f = IntegerField(min_value=2, max_value=4)
     202        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 2.']", f.clean, 1)
     203        self.assertEqual(3, f.clean(3))
     204        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 4.']", f.clean, 5)
     205
     206        f.min_value = 6
     207        f.max_value = 8
     208        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 6.']", f.clean, 3)
     209        self.assertEqual(7, f.clean(7))
     210        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 8.']", f.clean, 9)
     211
     212        f.min_value = None
     213        f.max_value = None
     214        self.assertEqual(1, f.clean(1))
     215        self.assertEqual(9, f.clean(9))
     216
    183217    # FloatField ##################################################################
    184218
    185219    def test_floatfield_1(self):
    class FieldsTests(TestCase):  
    217251        self.assertEqual(f.max_value, 1.5)
    218252        self.assertEqual(f.min_value, 0.5)
    219253
     254    def test_floatfield_validator_attributes(self):
     255        f = FloatField(min_value=1.5, max_value=2.5)
     256        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 1.5.']", f.clean, 1)
     257        self.assertEqual(2, f.clean(2))
     258        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 2.5.']", f.clean, 3)
     259
     260        f.min_value = 4
     261        f.max_value = 5
     262        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 4.']", f.clean, 3.5)
     263        self.assertEqual(4.5, f.clean(4.5))
     264        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 5.']", f.clean, 6.6)
     265
     266        f.min_value = None
     267        f.max_value = None
     268        self.assertEqual(0.5, f.clean(0.5))
     269        self.assertEqual(5.5, f.clean(5.5))
     270
    220271    # DecimalField ################################################################
    221272
    222273    def test_decimalfield_1(self):
    class FieldsTests(TestCase):  
    297348        self.assertEqual(f.clean('.01'), Decimal(".01"))
    298349        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure that there are no more than 0 digits before the decimal point.']", f.clean, '1.1')
    299350
     351    def test_decimalfield_validator_attributes(self):
     352        f = DecimalField(min_value=Decimal('1.5'), max_value=Decimal('2.5'))
     353        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 1.5.']", f.clean, 1)
     354        self.assertEqual(Decimal('2'), f.clean(2))
     355        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 2.5.']", f.clean, 3)
     356
     357        f.min_value = Decimal('4')
     358        f.max_value = Decimal('5')
     359        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 4.']", f.clean, 3.5)
     360        self.assertEqual(Decimal('4.5'), f.clean(4.5))
     361        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 5.']", f.clean, 6.6)
     362
     363        f.min_value = None
     364        f.max_value = None
     365        self.assertEqual(Decimal('0.5'), f.clean(0.5))
     366        self.assertEqual(Decimal('5.5'), f.clean(5.5))
     367
    300368    # DateField ###################################################################
    301369
    302370    def test_datefield_1(self):
    class FieldsTests(TestCase):  
    466534        self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 10 characters (it has 11).']", f.clean, '12345678901')
    467535        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '12345a')
    468536
     537    def test_regexfield_validator_attributes(self):
     538        f = RegexField('^\d+$')
     539        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, 'abc')
     540        self.assertEqual(u'1234', f.clean('1234'))
     541
     542        f.regex = '^[a-z]+$'
     543        self.assertEqual(u'abc', f.clean('abc'))
     544        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '1234')
     545
     546        f.regex = re.compile('^[^\w]+$')
     547        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, 'abc')
     548        self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '1234')
     549        self.assertEqual(u'--', f.clean('--'))
     550
     551        f.regex = None
     552        self.assertEqual(u'1-b-2', f.clean('1-b-2'))
     553
    469554    # EmailField ##################################################################
    470555
    471556    def test_emailfield_1(self):
Back to Top