Django ORM как получить результат по среднему средству

У меня есть модель, в которой я использую Django ORM для извлечения Avg значений из таблицы. Я хочу округлить значение Avg, как это сделать?

См. ниже. Я извлекаю среднюю цену от модели Цены, сгруппированные по дате в формате YYYY-MM, я хочу автоматически извлекать средние значения, округленные до ближайшего номера.

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

Используйте выражения Func().

Вот пример использования модели Book из тематического руководства Агрегирование Django для округления до двух десятичных знаков в SQLite:

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

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

Это позволяет параметризовать функцию округления (из ответа @RichardZschech):

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

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

Ответ 2

Улучшение ответа @mrts.

Это позволяет параметризовать функцию округления:

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

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

Ответ 3

Основываясь на предыдущих ответах, я пришел к этому решению, чтобы оно работало для 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

Мне нужно было иметь поддержку как PostgreSQL, так и SQLite, но также и возможность указать количество сохраняемых цифр.

Основываться на предыдущих ответах:

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))

Я бы предпочел что-то чище, как

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

но не нашел как, не стесняйся улучшать!

Ответ 5

Django не предоставляет раунд для запроса.

Я думаю, вам нужно сделать это вручную, используя ceil() или floor() из математического модуля.

http://docs.python.org/2/library/math.html#math.floor или http://docs.python.org/2/library/math.html#math.ceil

Итак, вы можете сделать:

import math
float_price_avg = rs.get('price', 0.) 
floor_price_avg = math.floor(rounded_price_avg)
ceil_price_avg = math.ceil(rounded_price_avg)