Fixed #33954 -- Prevented models.DecimalField from accepting NaN, Inf, and -Inf values.

This commit is contained in:
Mohamed Karam 2022-08-26 15:06:52 +02:00 committed by Mariusz Felisiak
parent 806e9e2d0d
commit b92ffebb0c
2 changed files with 24 additions and 14 deletions

View File

@ -2,7 +2,6 @@ import collections.abc
import copy
import datetime
import decimal
import math
import operator
import uuid
import warnings
@ -1703,22 +1702,24 @@ class DecimalField(Field):
def to_python(self, value):
if value is None:
return value
if isinstance(value, float):
if math.isnan(value):
raise exceptions.ValidationError(
self.error_messages["invalid"],
code="invalid",
params={"value": value},
)
return self.context.create_decimal_from_float(value)
try:
return decimal.Decimal(value)
if isinstance(value, float):
decimal_value = self.context.create_decimal_from_float(value)
else:
decimal_value = decimal.Decimal(value)
except (decimal.InvalidOperation, TypeError, ValueError):
raise exceptions.ValidationError(
self.error_messages["invalid"],
code="invalid",
params={"value": value},
)
if not decimal_value.is_finite():
raise exceptions.ValidationError(
self.error_messages["invalid"],
code="invalid",
params={"value": value},
)
return decimal_value
def get_db_prep_save(self, value, connection):
return connection.ops.adapt_decimalfield_value(

View File

@ -67,10 +67,19 @@ class DecimalFieldTests(TestCase):
def test_save_nan_invalid(self):
msg = "“nan” value must be a decimal number."
with self.assertRaisesMessage(ValidationError, msg):
BigD.objects.create(d=float("nan"))
with self.assertRaisesMessage(ValidationError, msg):
BigD.objects.create(d=math.nan)
for value in [float("nan"), math.nan, "nan"]:
with self.subTest(value), self.assertRaisesMessage(ValidationError, msg):
BigD.objects.create(d=value)
def test_save_inf_invalid(self):
msg = "“inf” value must be a decimal number."
for value in [float("inf"), math.inf, "inf"]:
with self.subTest(value), self.assertRaisesMessage(ValidationError, msg):
BigD.objects.create(d=value)
msg = "“-inf” value must be a decimal number."
for value in [float("-inf"), -math.inf, "-inf"]:
with self.subTest(value), self.assertRaisesMessage(ValidationError, msg):
BigD.objects.create(d=value)
def test_fetch_from_db_without_float_rounding(self):
big_decimal = BigD.objects.create(d=Decimal(".100000000000000000000000000005"))