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)