From e8c45963296eb8bf3938bf9ece30b585a8cbb097 Mon Sep 17 00:00:00 2001 From: Josh Schneier Date: Wed, 27 Sep 2017 09:42:04 -0400 Subject: [PATCH] Fixed #28562 -- Fixed DecimalValidator handling of positive exponent scientific notation. --- django/core/validators.py | 24 ++++++++++++------- .../field_tests/test_decimalfield.py | 9 +++---- tests/validators/tests.py | 1 + 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/django/core/validators.py b/django/core/validators.py index b36ab60704..07236b7d26 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -414,15 +414,21 @@ class DecimalValidator: def __call__(self, value): digit_tuple, exponent = value.as_tuple()[1:] - decimals = abs(exponent) - # digit_tuple doesn't include any leading zeros. - digits = len(digit_tuple) - if decimals > digits: - # We have leading zeros up to or past the decimal point. Count - # everything past the decimal point as a digit. We do not count - # 0 before the decimal point as a digit since that would mean - # we would not allow max_digits = decimal_places. - digits = decimals + if exponent >= 0: + # A positive exponent adds that many trailing zeros. + digits = len(digit_tuple) + exponent + decimals = 0 + else: + # If the absolute value of the negative exponent is larger than the + # number of digits, then it's the same as the number of digits, + # because it'll consume all of the digits in digit_tuple and then + # add abs(exponent) - len(digit_tuple) leading zeros after the + # decimal point. + if abs(exponent) > len(digit_tuple): + digits = decimals = abs(exponent) + else: + digits = len(digit_tuple) + decimals = abs(exponent) whole_digits = digits - decimals if self.max_digits is not None and digits > self.max_digits: diff --git a/tests/forms_tests/field_tests/test_decimalfield.py b/tests/forms_tests/field_tests/test_decimalfield.py index 1d5d27c8a0..d3fa222228 100644 --- a/tests/forms_tests/field_tests/test_decimalfield.py +++ b/tests/forms_tests/field_tests/test_decimalfield.py @@ -119,11 +119,12 @@ class DecimalFieldTest(FormFieldAssertionsMixin, SimpleTestCase): f.clean('1.1') def test_decimalfield_scientific(self): - f = DecimalField(max_digits=2, decimal_places=2) - self.assertEqual(f.clean('1E+2'), decimal.Decimal('1E+2')) - self.assertEqual(f.clean('1e+2'), decimal.Decimal('1E+2')) + f = DecimalField(max_digits=4, decimal_places=2) with self.assertRaisesMessage(ValidationError, "Ensure that there are no more"): - f.clean('0.546e+2') + f.clean('1E+2') + self.assertEqual(f.clean('1E+1'), decimal.Decimal('10')) + self.assertEqual(f.clean('1E-1'), decimal.Decimal('0.1')) + self.assertEqual(f.clean('0.546e+2'), decimal.Decimal('54.6')) def test_decimalfield_widget_attrs(self): f = DecimalField(max_digits=6, decimal_places=2) diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 4a2fe0b0c4..220219789d 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -265,6 +265,7 @@ TEST_DATA = [ (DecimalValidator(max_digits=3, decimal_places=1), Decimal('999'), ValidationError), (DecimalValidator(max_digits=4, decimal_places=1), Decimal('999'), None), (DecimalValidator(max_digits=20, decimal_places=2), Decimal('742403889818000000'), None), + (DecimalValidator(20, 2), Decimal('7.42403889818E+17'), None), (DecimalValidator(max_digits=20, decimal_places=2), Decimal('7424742403889818000000'), ValidationError), (DecimalValidator(max_digits=5, decimal_places=2), Decimal('7304E-1'), None), (DecimalValidator(max_digits=5, decimal_places=2), Decimal('7304E-3'), ValidationError),