[Answered ]-How to use @property decorator with annotate Django Query set

1👍

No, using .values(…) [Django-doc]/.values_list(…) [Django-doc] are often not a good idea anyway since this is a primitive obsession antipattern [refactoring.guru]: it will generate dictionaries (or tuples). These model objects thus no longer ship with all the logic you pass to the model. For example it can no longer follow ForeignKeys, you can not convert it to a string as specified in the model, etc.

Using .values(…) typically only has a few use-cases, one of these is to aggregate by a certain group of items, in which case you do not need properties anyway, but only the aggregate you are defining. You can limit the selection of columns with .only(…) [Django-doc] to reduce bandwidth. Regardless, the entire idea of using models is to ship data with "structure" and "extra logic" defined in the methods, properties, etc.

In this case you thus can sum these up with:

from django.db.models import F, Sum

Invoice.objects.annotate(
    total=Sum(
        F('invoiceitem__quantity')
        * F('invoiceitem__item_price__price')
        * (1 + F('invoiceitem__item_price__gst__gst') / 100)
    )
)

This will then for each Invoice add an attribute .total with the sum of the quantity times the price and the gst of all related InvoiceItems, or when rounded up to two decimals:

from django.db.models import F, Sum
from django.db.models.functions import Round

Invoice.objects.annotate(
    total=Sum(
        Round(
            F('invoiceitem__quantity')
            * F('invoiceitem__item_price__price')
            * (1 + F('invoiceitem__item_price__gst__gst') / 100),
            2,
        )
    )
)

this will do the rounding before summing up.

Leave a comment