diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 5381a3b17f..fd6969cd3d 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -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( diff --git a/tests/model_fields/test_decimalfield.py b/tests/model_fields/test_decimalfield.py index 912d55af72..88c6878f59 100644 --- a/tests/model_fields/test_decimalfield.py +++ b/tests/model_fields/test_decimalfield.py @@ -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"))