Skip to content

Commit 2831eae

Browse files
Samriddha9619charettesjacobtylerwalls
committed
Fixed #36233 -- Avoided quantizing integers stored in DecimalField on SQLite.
Co-authored-by: Simon Charette <charette.s@gmail.com> Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
1 parent e61a54d commit 2831eae

3 files changed

Lines changed: 27 additions & 3 deletions

File tree

django/db/backends/sqlite3/operations.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,26 +300,31 @@ def convert_timefield_value(self, value, expression, connection):
300300
value = parse_time(value)
301301
return value
302302

303+
@staticmethod
304+
def _create_decimal(value):
305+
if isinstance(value, (int, str)):
306+
return decimal.Decimal(value)
307+
return decimal.Context(prec=15).create_decimal_from_float(value)
308+
303309
def get_decimalfield_converter(self, expression):
304310
# SQLite stores only 15 significant digits. Digits coming from
305311
# float inaccuracy must be removed.
306-
create_decimal = decimal.Context(prec=15).create_decimal_from_float
307312
if isinstance(expression, Col):
308313
quantize_value = decimal.Decimal(1).scaleb(
309314
-expression.output_field.decimal_places
310315
)
311316

312317
def converter(value, expression, connection):
313318
if value is not None:
314-
return create_decimal(value).quantize(
319+
return self._create_decimal(value).quantize(
315320
quantize_value, context=expression.output_field.context
316321
)
317322

318323
else:
319324

320325
def converter(value, expression, connection):
321326
if value is not None:
322-
return create_decimal(value)
327+
return self._create_decimal(value)
323328

324329
return converter
325330

tests/model_fields/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def get_choices():
102102

103103
class BigD(models.Model):
104104
d = models.DecimalField(max_digits=32, decimal_places=30)
105+
large_int = models.DecimalField(max_digits=16, decimal_places=0, null=True)
105106

106107

107108
class FloatModel(models.Model):

tests/model_fields/test_decimalfield.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.core import validators
66
from django.core.exceptions import ValidationError
77
from django.db import connection, models
8+
from django.db.models import Max
89
from django.test import TestCase
910

1011
from .models import BigD, Foo
@@ -140,3 +141,20 @@ def test_roundtrip_with_trailing_zeros(self):
140141
obj = Foo.objects.create(a="bar", d=Decimal("8.320"))
141142
obj.refresh_from_db()
142143
self.assertEqual(obj.d.compare_total(Decimal("8.320")), Decimal("0"))
144+
145+
def test_large_integer_precision(self):
146+
large_int_val = Decimal("9999999999999999")
147+
obj = BigD.objects.create(large_int=large_int_val, d=Decimal("0"))
148+
obj.refresh_from_db()
149+
self.assertEqual(obj.large_int, large_int_val)
150+
151+
def test_large_integer_precision_aggregation(self):
152+
large_int_val = Decimal("9999999999999999")
153+
BigD.objects.create(large_int=large_int_val, d=Decimal("0"))
154+
result = BigD.objects.aggregate(max_val=Max("large_int"))
155+
self.assertEqual(result["max_val"], large_int_val)
156+
157+
def test_roundtrip_integer_with_trailing_zeros(self):
158+
obj = Foo.objects.create(a="bar", d=Decimal("8"))
159+
obj.refresh_from_db()
160+
self.assertEqual(obj.d.compare_total(Decimal("8.000")), Decimal("0"))

0 commit comments

Comments
 (0)