Fixed #28164 -- Improved float conversions in DecimalField.to_python

Thanks Tim Graham and Adam Johnson for the reviews.
This commit is contained in:
Claude Paroz 2017-05-03 11:40:09 +02:00
parent d842ada305
commit a87189fc5e
2 changed files with 12 additions and 0 deletions

View File

@ -1508,6 +1508,10 @@ class DecimalField(Field):
validators.DecimalValidator(self.max_digits, self.decimal_places) validators.DecimalValidator(self.max_digits, self.decimal_places)
] ]
@cached_property
def context(self):
return decimal.Context(prec=self.max_digits)
def deconstruct(self): def deconstruct(self):
name, path, args, kwargs = super().deconstruct() name, path, args, kwargs = super().deconstruct()
if self.max_digits is not None: if self.max_digits is not None:
@ -1522,6 +1526,8 @@ class DecimalField(Field):
def to_python(self, value): def to_python(self, value):
if value is None: if value is None:
return value return value
if isinstance(value, float):
return self.context.create_decimal_from_float(value)
try: try:
return decimal.Decimal(value) return decimal.Decimal(value)
except decimal.InvalidOperation: except decimal.InvalidOperation:

View File

@ -15,6 +15,12 @@ class DecimalFieldTests(TestCase):
f = models.DecimalField(max_digits=4, decimal_places=2) f = models.DecimalField(max_digits=4, decimal_places=2)
self.assertEqual(f.to_python(3), Decimal('3')) self.assertEqual(f.to_python(3), Decimal('3'))
self.assertEqual(f.to_python('3.14'), Decimal('3.14')) self.assertEqual(f.to_python('3.14'), Decimal('3.14'))
# to_python() converts floats and honors max_digits.
self.assertEqual(f.to_python(3.1415926535897), Decimal('3.142'))
self.assertEqual(f.to_python(2.4), Decimal('2.400'))
# Uses default rounding of ROUND_HALF_EVEN.
self.assertEqual(f.to_python(2.0625), Decimal('2.062'))
self.assertEqual(f.to_python(2.1875), Decimal('2.188'))
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
f.to_python('abc') f.to_python('abc')