Kaydet (Commit) 267a1dcd authored tarafından Josh Smeaton's avatar Josh Smeaton Kaydeden (comit) Tim Graham

Fixed #23941 -- Removed implicit decimal formatting from expressions.

üst e2868308
......@@ -1195,8 +1195,6 @@ class BaseDatabaseOperations(object):
Transform a decimal.Decimal value to an object compatible with what is
expected by the backend driver for decimal (numeric) columns.
"""
if value is None:
return None
return utils.format_number(value, max_digits, decimal_places)
def year_lookup_bounds_for_date_field(self, value):
......
......@@ -312,9 +312,7 @@ WHEN (new.%(col_name)s IS NULL)
return value
def convert_decimalfield_value(self, value, field):
if value is not None:
value = backend_utils.typecast_decimal(field.format_number(value))
return value
return backend_utils.typecast_decimal(field.format_number(value))
# cx_Oracle always returns datetime.datetime objects for
# DATE and TIMESTAMP columns, but Django wants to see a
......
......@@ -277,9 +277,7 @@ class DatabaseOperations(BaseDatabaseOperations):
return converters
def convert_decimalfield_value(self, value, field):
if value is not None:
value = backend_utils.typecast_decimal(field.format_number(value))
return value
return backend_utils.typecast_decimal(field.format_number(value))
def convert_datefield_value(self, value, field):
if value is not None and not isinstance(value, datetime.date):
......
......@@ -191,9 +191,18 @@ def format_number(value, max_digits, decimal_places):
Formats a number into a string with the requisite number of digits and
decimal places.
"""
if value is None:
return None
if isinstance(value, decimal.Decimal):
context = decimal.getcontext().copy()
context.prec = max_digits
return "{0:f}".format(value.quantize(decimal.Decimal(".1") ** decimal_places, context=context))
else:
if max_digits is not None:
context.prec = max_digits
if decimal_places is not None:
value = value.quantize(decimal.Decimal(".1") ** decimal_places, context=context)
else:
context.traps[decimal.Rounded] = 1
value = context.create_decimal(value)
return "{:f}".format(value)
if decimal_places is not None:
return "%.*f" % (decimal_places, value)
return "{:f}".format(value)
......@@ -255,7 +255,7 @@ class ExpressionNode(CombinableMixin):
elif internal_type.endswith('IntegerField'):
return int(value)
elif internal_type == 'DecimalField':
return backend_utils.typecast_decimal(field.format_number(value))
return backend_utils.typecast_decimal(value)
return value
def get_lookup(self, lookup):
......
......@@ -1536,7 +1536,7 @@ class DecimalField(Field):
)
def _format(self, value):
if isinstance(value, six.string_types) or value is None:
if isinstance(value, six.string_types):
return value
else:
return self.format_number(value)
......
......@@ -257,7 +257,10 @@ placeholder within the ``template``.
The ``output_field`` argument requires a model field instance, like
``IntegerField()`` or ``BooleanField()``, into which Django will load the value
after it's retrieved from the database.
after it's retrieved from the database. Usually no arguments are needed when
instantiating the model field as any arguments relating to data validation
(``max_length``, ``max_digits``, etc.) will not be enforced on the expression's
output value.
Note that ``output_field`` is only required when Django is unable to determine
what field type the result should be. Complex expressions that mix field types
......@@ -318,8 +321,10 @@ values into their corresponding database type.
The ``output_field`` argument should be a model field instance, like
``IntegerField()`` or ``BooleanField()``, into which Django will load the value
after it's retrieved from the database.
after it's retrieved from the database. Usually no arguments are needed when
instantiating the model field as any arguments relating to data validation
(``max_length``, ``max_digits``, etc.) will not be enforced on the expression's
output value.
Technical Information
=====================
......
......@@ -17,7 +17,7 @@ with warnings.catch_warnings(record=True) as w:
from django.test import TestCase
from django.test.utils import Approximate
from django.test.utils import CaptureQueriesContext
from django.utils import six
from django.utils import six, timezone
from django.utils.deprecation import RemovedInDjango20Warning
from .models import Author, Publisher, Book, Store
......@@ -689,6 +689,19 @@ class BaseAggregateTestCase(TestCase):
self.assertNotIn('order by', qstr)
self.assertEqual(qstr.count(' join '), 0)
def test_decimal_max_digits_has_no_effect(self):
Book.objects.all().delete()
a1 = Author.objects.first()
p1 = Publisher.objects.first()
thedate = timezone.now()
for i in range(10):
Book.objects.create(
isbn="abcde{}".format(i), name="none", pages=10, rating=4.0,
price=9999.98, contact=a1, publisher=p1, pubdate=thedate)
book = Book.objects.aggregate(price_sum=Sum('price'))
self.assertEqual(book['price_sum'], Decimal("99999.80"))
class ComplexAggregateTestCase(TestCase):
fixtures = ["aggregation.json"]
......@@ -755,8 +768,8 @@ class ComplexAggregateTestCase(TestCase):
self.assertEqual(b2.sums, 383.69)
b3 = Book.objects.annotate(sums=Sum(F('rating') + F('pages') + F('price'),
output_field=DecimalField(max_digits=6, decimal_places=2))).get(pk=4)
self.assertEqual(b3.sums, Decimal("383.69"))
output_field=DecimalField())).get(pk=4)
self.assertEqual(b3.sums, Approximate(Decimal("383.69"), places=2))
def test_complex_aggregations_require_kwarg(self):
with six.assertRaisesRegex(self, TypeError, 'Complex expressions require an alias'):
......
......@@ -4,7 +4,7 @@ from __future__ import unicode_literals
import copy
import datetime
from decimal import Decimal
from decimal import Decimal, Rounded
import re
import threading
import unittest
......@@ -1059,6 +1059,22 @@ class BackendUtilTests(TestCase):
'0.1')
equal('0.1234567890', 12, 0,
'0')
equal('0.1234567890', None, 0,
'0')
equal('1234567890.1234567890', None, 0,
'1234567890')
equal('1234567890.1234567890', None, 2,
'1234567890.12')
equal('0.1234', 5, None,
'0.1234')
equal('123.12', 5, None,
'123.12')
with self.assertRaises(Rounded):
equal('0.1234567890', 5, None,
'0.12346')
with self.assertRaises(Rounded):
equal('1234567890.1234', 5, None,
'1234600000')
class DBTestSettingsRenamedTests(IgnoreAllDeprecationWarningsMixin, TestCase):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment