mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -14,6 +14,10 @@ from django.utils.translation import ugettext as _, ugettext_lazy, ungettext | |||||||
| from django.utils.functional import Promise, lazy | from django.utils.functional import Promise, lazy | ||||||
| from django.utils.encoding import force_unicode | from django.utils.encoding import force_unicode | ||||||
| import re | 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}' | _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])?' | _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"(^[-!#$%&'*+/=?^_`{}|~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'|^"([\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 |     r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)  # domain | ||||||
| decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') |  | ||||||
| integer_re = re.compile(r'^-?\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}$') | 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) | 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 |         self.max_digits, self.decimal_places = max_digits, decimal_places | ||||||
|  |  | ||||||
|     def __call__(self, field_data, all_data): |     def __call__(self, field_data, all_data): | ||||||
|         match = decimal_re.search(str(field_data)) |         try: | ||||||
|         if not match: |             val = Decimal(field_data) | ||||||
|  |         except DecimalException: | ||||||
|             raise ValidationError, _("Please enter a valid decimal number.") |             raise ValidationError, _("Please enter a valid decimal number.") | ||||||
|          |  | ||||||
|         digits = len(match.group('digits') or '') |         pieces = str(val).split('.') | ||||||
|         decimals = len(match.group('decimals') or '') |         decimals = (len(pieces) == 2) and len(pieces[1]) or 0 | ||||||
|          |         digits = len(pieces[0]) | ||||||
|  |  | ||||||
|         if digits + decimals > self.max_digits: |         if digits + decimals > self.max_digits: | ||||||
|             raise ValidationError, ungettext("Please enter a valid decimal number with at most %s total digit.", |             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 |                 "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits | ||||||
|   | |||||||
| @@ -12,6 +12,11 @@ from django.utils.encoding import smart_unicode | |||||||
| from util import ErrorList, ValidationError | from util import ErrorList, ValidationError | ||||||
| from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple | 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__ = ( | __all__ = ( | ||||||
|     'Field', 'CharField', 'IntegerField', |     'Field', 'CharField', 'IntegerField', | ||||||
|     'DEFAULT_DATE_INPUT_FORMATS', 'DateField', |     '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) |             raise ValidationError(ugettext('Ensure this value is greater than or equal to %s.') % self.min_value) | ||||||
|         return value |         return value | ||||||
|  |  | ||||||
| decimal_re = re.compile(r'^-?(?P<digits>\d+)(\.(?P<decimals>\d+))?$') |  | ||||||
|  |  | ||||||
| class DecimalField(Field): | class DecimalField(Field): | ||||||
|     def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): |     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 |         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: |         if not self.required and value in EMPTY_VALUES: | ||||||
|             return None |             return None | ||||||
|         value = value.strip() |         value = value.strip() | ||||||
|         match = decimal_re.search(value) |         try: | ||||||
|         if not match: |  | ||||||
|             raise ValidationError(ugettext('Enter a number.')) |  | ||||||
|         else: |  | ||||||
|             value = Decimal(value) |             value = Decimal(value) | ||||||
|         digits = len(match.group('digits') or '') |         except DecimalException: | ||||||
|         decimals = len(match.group('decimals') or '') |             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: |         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) |             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: |         if self.min_value is not None and value < self.min_value: | ||||||
|   | |||||||
| @@ -1176,6 +1176,10 @@ ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] | |||||||
| Decimal("1.5") | Decimal("1.5") | ||||||
| >>> f.clean('0.5') | >>> f.clean('0.5') | ||||||
| Decimal("0.5") | Decimal("0.5") | ||||||
|  | >>> f.clean('.5') | ||||||
|  | Decimal("0.5") | ||||||
|  | >>> f.clean('00.50') | ||||||
|  | Decimal("0.50") | ||||||
|  |  | ||||||
| # DateField ################################################################### | # DateField ################################################################### | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user