Inside an annotation I do some calculations, and I want the output to be a decimal, with max 8 digits and max 2 decimal. I don't know why but Django ignores decimal_places and max_digits. Here is my code:
Order.objects.all().annotate(
amount=Coalesce(
Sum(
Case(
When(
Q(payments__status='complete'),
then=F('payments__amount') - (
F('payments__amount') * F('payments__vat')/100
)
), output_field=DecimalField(decimal_places=2, max_digits=8)
)
), 0)
).values('amount')
output = 12.5999999999999996447286321199499070644378662109375
I'm using Django 1.9.5
DecimalField – Django Models Last Updated : 12 Feb, 2020 DecimalField is a field which stores a fixed-precision decimal number, represented in Python by a Decimal instance. It validates the input using DecimalValidator.
Show activity on this post. DecimalField accepts arguments for max_digits which is the total number of digits in a decimal number and for decimal_places which is the number of decimal places. If you define max_digits to be 3 and decimal_places to be 2, the largest number you can save is 9.99.
Default is False. If True, the field is allowed to be blank. Default is False. The name of the database column to use for this field. If this isn’t given, Django will use the field’s name. The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.
It validates the input using DecimalValidator. The maximum number of digits allowed in the number. Note that this number must be greater than or equal to decimal_places. The number of decimal places to store with the number. For example, to store numbers up to 999 with a resolution of 2 decimal places, you’d use:
I had had the same issue in my job, to solve this I've created the following custom aggregate:
class SumDecimal(Func):
function = 'SUM'
name = 'sum'
contains_aggregate = True
output_field = DecimalField()
template = '%(function)s(%(expressions)s)'
def __init__(self, *args, **kwargs):
self.decimal_places = kwargs.pop('decimal_places', None)
super(SumDecimal, self).__init__(*args, **kwargs)
def as_sql(self, compiler, connection, function=None, template=None):
sql = super(SumDecimal, self).as_sql(
compiler=compiler,
connection=connection,
function=function,
template=template)
if self.decimal_places:
sql, params = sql
sql = 'CAST(%s AS DECIMAL(16, %d))' % (sql, self.decimal_places)
return sql, params
return sql
To use this is pretty simple, just use like:
mymodel.objects.all().annotate(sum_value=SumDecimal('value', decimal_places=2))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With