From ebc4ee3369694e6dca5cf216d4176bdefd930fd6 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 22 Dec 2017 05:50:56 +0500 Subject: [PATCH] Refs #23941 -- Prevented incorrect rounding of DecimalField annotations on SQLite. --- django/db/backends/sqlite3/operations.py | 9 +++++++-- tests/annotations/tests.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index 61514a7642..b58c344e6b 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -1,12 +1,13 @@ import datetime +import decimal import uuid -from decimal import Decimal from django.conf import settings from django.core.exceptions import FieldError from django.db import utils from django.db.backends.base.operations import BaseDatabaseOperations from django.db.models import aggregates, fields +from django.db.models.expressions import Col from django.utils import timezone from django.utils.dateparse import parse_date, parse_datetime, parse_time from django.utils.duration import duration_string @@ -242,8 +243,12 @@ class DatabaseOperations(BaseDatabaseOperations): def convert_decimalfield_value(self, value, expression, connection): if value is not None: + if not isinstance(expression, Col): + # SQLite stores only 15 significant digits. Digits coming from + # float inaccuracy must be removed. + return decimal.Context(prec=15).create_decimal_from_float(value) value = expression.output_field.format_number(value) - value = Decimal(value) + return decimal.Decimal(value) return value def convert_uuidfield_value(self, value, expression, connection): diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index aaf56e79ef..d2e2214db8 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -244,6 +244,20 @@ class NonAggregateAnnotationTestCase(TestCase): sum_rating=Sum('rating') ).filter(sum_rating=F('nope'))) + def test_decimal_annotation(self): + salary = Decimal(10) ** -Employee._meta.get_field('salary').decimal_places + Employee.objects.create( + first_name='Max', + last_name='Paine', + store=Store.objects.first(), + age=23, + salary=salary, + ) + self.assertEqual( + Employee.objects.annotate(new_salary=F('salary') / 10).get().new_salary, + salary / 10, + ) + def test_filter_decimal_annotation(self): qs = Book.objects.annotate(new_price=F('price') + 1).filter(new_price=Decimal(31)).values_list('new_price') self.assertEqual(qs.get(), (Decimal(31),))