From 92f54aff7a1ea4128906337dcd570a7b897f8976 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Fri, 13 Jul 2007 09:09:59 +0000 Subject: [PATCH] Fixed #4807 -- Fixed a couple of corner cases in decimal form input validation. Based on a suggestion from Chriss Moffit. git-svn-id: http://code.djangoproject.com/svn/django/trunk@5680 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/validators.py | 19 ++++++++++++------- django/newforms/fields.py | 19 +++++++++++-------- tests/regressiontests/forms/tests.py | 4 ++++ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/django/core/validators.py b/django/core/validators.py index 4602c44588..ca436690a5 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -14,6 +14,10 @@ from django.utils.translation import ugettext as _, ugettext_lazy, ungettext from django.utils.functional import Promise, lazy from django.utils.encoding import force_unicode import re +try: + from decimal import Decimal, DecimalException +except ImportError: + from django.utils._decimal import Decimal, DecimalException # Python 2.3 _datere = r'\d{4}-\d{1,2}-\d{1,2}' _timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' @@ -26,7 +30,6 @@ 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-Z]{2,6}$', re.IGNORECASE) # domain -decimal_re = re.compile(r'^-?(?P\d+)(\.(?P\d+))?$') integer_re = re.compile(r'^-?\d+$') ip4_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}$') phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) @@ -415,13 +418,15 @@ class IsValidDecimal(object): self.max_digits, self.decimal_places = max_digits, decimal_places def __call__(self, field_data, all_data): - match = decimal_re.search(str(field_data)) - if not match: + try: + val = Decimal(field_data) + except DecimalException: raise ValidationError, _("Please enter a valid decimal number.") - - digits = len(match.group('digits') or '') - decimals = len(match.group('decimals') or '') - + + pieces = str(val).split('.') + decimals = (len(pieces) == 2) and len(pieces[1]) or 0 + digits = len(pieces[0]) + if digits + decimals > self.max_digits: raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.", "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits diff --git a/django/newforms/fields.py b/django/newforms/fields.py index 507e763353..e4982e1464 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -12,6 +12,11 @@ from django.utils.encoding import smart_unicode from util import ErrorList, ValidationError from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple +try: + from decimal import Decimal, DecimalException +except ImportError: + from django.utils._decimal import Decimal, DecimalException + __all__ = ( 'Field', 'CharField', 'IntegerField', 'DEFAULT_DATE_INPUT_FORMATS', 'DateField', @@ -162,8 +167,6 @@ class FloatField(Field): raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value) return value -decimal_re = re.compile(r'^-?(?P\d+)(\.(?P\d+))?$') - class DecimalField(Field): def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): self.max_value, self.min_value = max_value, min_value @@ -181,13 +184,13 @@ class DecimalField(Field): if not self.required and value in EMPTY_VALUES: return None value = value.strip() - match = decimal_re.search(value) - if not match: - raise ValidationError(ugettext('Enter a number.')) - else: + try: value = Decimal(value) - digits = len(match.group('digits') or '') - decimals = len(match.group('decimals') or '') + except DecimalException: + raise ValidationError(ugettext('Enter a number.')) + pieces = str(value).split('.') + decimals = (len(pieces) == 2) and len(pieces[1]) or 0 + digits = len(pieces[0]) if self.max_value is not None and value > self.max_value: raise ValidationError(ugettext('Ensure this value is less than or equal to %s.') % self.max_value) if self.min_value is not None and value < self.min_value: diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 67d527a08e..e612e6a943 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -1176,6 +1176,10 @@ ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] Decimal("1.5") >>> f.clean('0.5') Decimal("0.5") +>>> f.clean('.5') +Decimal("0.5") +>>> f.clean('00.50') +Decimal("0.50") # DateField ###################################################################