diff --git a/django/core/validators.py b/django/core/validators.py index 6001a825ee..c5e7c23afb 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -14,14 +14,6 @@ except ImportError: # It's OK if Django settings aren't configured. URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)' -url_re = re.compile( - r'^https?://' # http:// or https:// - r'(?:(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}|' #domain... - r'localhost|' #localhost... - r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip - r'(?::\d+)?' # optional port - r'(?:/?|/\S+)$', re.IGNORECASE) - class RegexValidator(object): regex = '' message = _(u'Enter a valid value.') @@ -46,7 +38,13 @@ class RegexValidator(object): raise ValidationError(self.message, code=self.code) class URLValidator(RegexValidator): - regex = url_re + regex = re.compile( + r'^https?://' # http:// or https:// + r'(?:(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}|' #domain... + r'localhost|' #localhost... + r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip + r'(?::\d+)?' # optional port + r'(?:/?|/\S+)$', re.IGNORECASE) def __init__(self, verify_exists=False, validator_user_agent=URL_VALIDATOR_USER_AGENT): super(URLValidator, self).__init__() @@ -72,97 +70,70 @@ class URLValidator(RegexValidator): except: # urllib2.URLError, httplib.InvalidURL, etc. raise ValidationError(_(u'This URL appears to be a broken link.'), code='invalid_link') + def validate_integer(value): try: int(value) except (ValueError, TypeError), e: raise ValidationError('') + email_re = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string r')@(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain - -def validate_email(value): - if not email_re.search(smart_unicode(value)): - raise ValidationError(_(u'Enter a valid e-mail address.'), code='invalid') +validate_email = RegexValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid') slug_re = re.compile(r'^[-\w]+$') - -def validate_slug(value): - if not slug_re.search(smart_unicode(value)): - raise ValidationError( - _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), - code='invalid' - ) +validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid') ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') - -def validate_ipv4_address(value): - if not ipv4_re.search(smart_unicode(value)): - raise ValidationError( - _(u'Enter a valid IPv4 address.'), - code="invalid" - ) +validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid') comma_separated_int_list_re = re.compile('^[\d,]+$') +validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid') -def validate_comma_separated_integer_list(value): - if not comma_separated_int_list_re.search(smart_unicode(value)): - raise ValidationError( - _(u'Enter only digits separated by commas.'), - code="invalid" - ) -class MaxValueValidator(object): - def __init__(self, max_value): - self.max_value = max_value +class BaseValidator(object): + compare = lambda self, a, b: a is b + clean = lambda self, x: x + message = _(u'Ensure this value is %(limit_value)s (it is %(show_value)s).') + code = 'limit_value' + + def __init__(self, limit_value): + self.limit_value = limit_value def __call__(self, value): - if value > self.max_value: + cleaned = self.clean(value) + if self.compare(cleaned, self.limit_value): raise ValidationError( - _(u'Ensure this value is less than or equal to %s.') % self.max_value, - code='max_value', - params=(self.max_value,) + self.message, + code=self.code, + params={'limit_value': self.limit_value, 'show_value': cleaned} ) -class MinValueValidator(object): - def __init__(self, min_value): - self.min_value = min_value +class MaxValueValidator(BaseValidator): + compare = lambda self, a, b: a > b + message = _(u'Ensure this value is less than or equal to %(limit_value)s.') + code = 'max_value' - def __call__(self, value): - if value < self.min_value: - raise ValidationError( - _(u'Ensure this value is greater than or equal to %s.') % self.min_value, - code='min_value', - params=(self.min_value,) - ) +class MinValueValidator(BaseValidator): + compare = lambda self, a, b: a < b + message = _(u'Ensure this value is greater than or equal to %(limit_value)s.') + code = 'min_value' -class MinLengthValidator(object): - def __init__(self, min_length): - self.min_length = min_length +class MinLengthValidator(BaseValidator): + compare = lambda self, a, b: a < b + clean = lambda self, x: len(x) + message = _(u'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).') + code = 'min_length' - def __call__(self, value): - value_len = len(value) - if value_len < self.min_length: - raise ValidationError( - _(u'Ensure this value has at least %(min)d characters (it has %(length)d).'), - code='min_length', - params={ 'min': self.min_length, 'length': value_len} - ) +class MaxLengthValidator(BaseValidator): + compare = lambda self, a, b: a > b + clean = lambda self, x: len(x) + message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).') + code = 'max_length' -class MaxLengthValidator(object): - def __init__(self, max_length): - self.max_length = max_length - - def __call__(self, value): - value_len = len(value) - if value_len > self.max_length: - raise ValidationError( - _(u'Ensure this value has at most %(max)d characters (it has %(length)d).'), - code='max_length', - params={ 'max': self.max_length, 'length': value_len} - ) class ComplexValidator(object): def get_value(self, name, all_values, obj): @@ -185,3 +156,4 @@ class RequiredIfOtherFieldBlank(ComplexValidator): if self.get_value(self.other_field, all_values, obj) in EMPTY_VALUES: if value in EMPTY_VALUES: raise ValidationError('This field is required if %s is blank.' % self.other_field) + diff --git a/django/forms/fields.py b/django/forms/fields.py index 38ae2cec19..4b4594a5c2 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -204,8 +204,8 @@ class CharField(Field): class IntegerField(Field): default_error_messages = { 'invalid': _(u'Enter a whole number.'), - 'max_value': _(u'Ensure this value is less than or equal to %s.'), - 'min_value': _(u'Ensure this value is greater than or equal to %s.'), + 'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'), + 'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'), } def __init__(self, max_value=None, min_value=None, *args, **kwargs): @@ -254,8 +254,8 @@ class FloatField(IntegerField): class DecimalField(Field): default_error_messages = { 'invalid': _(u'Enter a number.'), - 'max_value': _(u'Ensure this value is less than or equal to %s.'), - 'min_value': _(u'Ensure this value is greater than or equal to %s.'), + 'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'), + 'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'), 'max_digits': _('Ensure that there are no more than %s digits in total.'), 'max_decimal_places': _('Ensure that there are no more than %s decimal places.'), 'max_whole_digits': _('Ensure that there are no more than %s digits before the decimal point.') diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py index ec91b57a06..3f6cfcac80 100644 --- a/tests/regressiontests/forms/error_messages.py +++ b/tests/regressiontests/forms/error_messages.py @@ -26,8 +26,8 @@ ValidationError: [u'LENGTH 11, MAX LENGTH 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> f = IntegerField(min_value=5, max_value=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -50,8 +50,8 @@ ValidationError: [u'MAX VALUE IS 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> f = FloatField(min_value=5, max_value=10, error_messages=e) >>> f.clean('') Traceback (most recent call last): @@ -74,8 +74,8 @@ ValidationError: [u'MAX VALUE IS 10'] >>> e = {'required': 'REQUIRED'} >>> e['invalid'] = 'INVALID' ->>> e['min_value'] = 'MIN VALUE IS %s' ->>> e['max_value'] = 'MAX VALUE IS %s' +>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s' +>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s' >>> e['max_digits'] = 'MAX DIGITS IS %s' >>> e['max_decimal_places'] = 'MAX DP IS %s' >>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s'