Django ORM how to Round an Avg result

半城伤御伤魂 提交于 2019-11-26 17:01:11

问题


I have a model in which I use Django ORM to extract Avg of values from the table. I want to Round that Avg value, how do I do this?

See below I am extracting Avg price from Prices model grouped by date in format YYYY-MM, I want to automatically extract the average values rounded to the closest number.

rs = Prices.objects.all.extra(select={
    'for_date': 'CONCAT(CONCAT(extract( YEAR from for_date ), "-"),
        LPAD(extract(MONTH from for_date), 2, "00"))'
    }).values('for_date').annotate(price=Avg('price')).order_by('-for_date')

回答1:


Use Func() expressions.

Here's an example using the Book model from Django Aggregation topic guide to round to two decimal places in SQLite:

class Round(Func):
    function = 'ROUND'
    template='%(function)s(%(expressions)s, 2)'

Book.objects.all().aggregate(Round(Avg('price')))

This allows the round function to be parameterised (from @RichardZschech's answer):

class Round(Func):
  function = 'ROUND'
  arity = 2

Book.objects.all().aggregate(Round(Avg('price'), 2))



回答2:


Improving on @mrts answer.

This allows the round function to be parameterised:

class Round(Func):
  function = 'ROUND'
  arity = 2

Book.objects.all().aggregate(Round(Avg('price'), 2))



回答3:


Building on previous answers, I've come to this solution to make it work for PostgreSQL:

from django.db.models import Func

class Round2(Func):
    function = "ROUND"
    template = "%(function)s(%(expressions)s::numeric, 2)"

# Then use it as ,e.g.:
# queryset.annotate(ag_roi=Round2("roi"))

# qs.aggregate(ag_sold_pct=Round2(Sum("sold_uts") / (1.0 * Sum("total_uts"))) * 100



回答4:


I needed to have both PostgreSQL and SQLite support, but also keep the ability to specify the number of digit to keep.

Build on previous answers :

class Round(Func):
    function = 'ROUND'
    arity = 2
    # Only works as the arity is 2
    arg_joiner = '::numeric, '

    def as_sqlite(self, compiler, connection, **extra_context):
        return super().as_sqlite(compiler, connection, arg_joiner=", ", **extra_context)

# Then one can use it as:
# queryset.annotate(avg_val=Round(AVG("val"), 6))

I would have prefered something cleaner like

if SQLITE:
    arg_joiner=", "
elif PGSQL:
    arg_joiner = '::numeric, '
else raise NotImplemented()

but did not find how, feel free to improve !



来源:https://stackoverflow.com/questions/13793759/django-orm-how-to-round-an-avg-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!