Commit 38a1b3ec by Tom Christie

Rationalize decimal logic. Closes #3222.

parent f7d44dfa
...@@ -797,6 +797,11 @@ class DecimalField(Field): ...@@ -797,6 +797,11 @@ class DecimalField(Field):
self.max_value = max_value self.max_value = max_value
self.min_value = min_value self.min_value = min_value
if self.max_digits is not None and self.decimal_places is not None:
self.max_whole_digits = self.max_digits - self.decimal_places
else:
self.max_whole_digits = None
super(DecimalField, self).__init__(**kwargs) super(DecimalField, self).__init__(**kwargs)
if self.max_value is not None: if self.max_value is not None:
...@@ -840,24 +845,29 @@ class DecimalField(Field): ...@@ -840,24 +845,29 @@ class DecimalField(Field):
values or to enhance it in any way you need to. values or to enhance it in any way you need to.
""" """
sign, digittuple, exponent = value.as_tuple() sign, digittuple, exponent = value.as_tuple()
decimals = exponent * decimal.Decimal(-1) if exponent < 0 else 0
if exponent >= 0:
# digittuple doesn't include any leading zeros. # 1234500.0
digits = len(digittuple) total_digits = len(digittuple) + exponent
if decimals > digits: whole_digits = total_digits
# We have leading zeros up to or past the decimal point. Count decimal_places = 0
# everything past the decimal point as a digit. We do not count elif len(digittuple) > abs(exponent):
# 0 before the decimal point as a digit since that would mean # 123.45
# we would not allow max_digits = decimal_places. total_digits = len(digittuple)
digits = decimals whole_digits = total_digits - abs(exponent)
whole_digits = digits - decimals decimal_places = abs(exponent)
else:
if self.max_digits is not None and digits > self.max_digits: # 0.001234
total_digits = abs(exponent)
whole_digits = 0
decimal_places = total_digits
if self.max_digits is not None and total_digits > self.max_digits:
self.fail('max_digits', max_digits=self.max_digits) self.fail('max_digits', max_digits=self.max_digits)
if self.decimal_places is not None and decimals > self.decimal_places: if self.decimal_places is not None and decimal_places > self.decimal_places:
self.fail('max_decimal_places', max_decimal_places=self.decimal_places) self.fail('max_decimal_places', max_decimal_places=self.decimal_places)
if self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places): if self.max_whole_digits is not None and whole_digits > self.max_whole_digits:
self.fail('max_whole_digits', max_whole_digits=self.max_digits - self.decimal_places) self.fail('max_whole_digits', max_whole_digits=self.max_whole_digits)
return value return value
......
...@@ -766,15 +766,17 @@ class TestDecimalField(FieldValues): ...@@ -766,15 +766,17 @@ class TestDecimalField(FieldValues):
0: Decimal('0'), 0: Decimal('0'),
12.3: Decimal('12.3'), 12.3: Decimal('12.3'),
0.1: Decimal('0.1'), 0.1: Decimal('0.1'),
'2E+2': Decimal('200'), '2E+1': Decimal('20'),
} }
invalid_inputs = ( invalid_inputs = (
('abc', ["A valid number is required."]), ('abc', ["A valid number is required."]),
(Decimal('Nan'), ["A valid number is required."]), (Decimal('Nan'), ["A valid number is required."]),
(Decimal('Inf'), ["A valid number is required."]), (Decimal('Inf'), ["A valid number is required."]),
('12.345', ["Ensure that there are no more than 3 digits in total."]), ('12.345', ["Ensure that there are no more than 3 digits in total."]),
(200000000000.0, ["Ensure that there are no more than 3 digits in total."]),
('0.01', ["Ensure that there are no more than 1 decimal places."]), ('0.01', ["Ensure that there are no more than 1 decimal places."]),
(123, ["Ensure that there are no more than 2 digits before the decimal point."]) (123, ["Ensure that there are no more than 2 digits before the decimal point."]),
('2E+2', ["Ensure that there are no more than 2 digits before the decimal point."])
) )
outputs = { outputs = {
'1': '1.0', '1': '1.0',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment