Я использую агрегатное выражение запроса Django для суммирования некоторых значений. Конечное значение - это выражение деления, которое иногда может содержать ноль в качестве знаменателя. Мне нужен способ уйти, если это так, чтобы он просто возвращал 0.

Я пробовал следующее, так как использовал что-то похожее на мои выражения аннотации:

from django.db.models import Sum, F, FloatField, Case, When

def for_period(self, start_date, end_date):
    return self.model.objects.filter(
        date__range=(start_date, end_date)
    ).aggregate(
        sales=Sum(F("value")),
        purchase_cogs=Sum(F('purchase_cogs')),
        direct_cogs=Sum(F("direct_cogs")),
        profit=Sum(F('profit'))
    ).aggregate(
        margin=Case(
            When(sales=0, then=0),
            default=(Sum(F('profit')) / Sum(F('value')))*100
        )
    )

Я пробовал следующее, так как использовал что-то похожее на мои выражения аннотации:…

объект 'dict' не имеет атрибута 'aggregate'

Как правильно с этим справиться?

11
Adam Starrh 7 Сен 2016 в 18:33

3 ответа

Лучший ответ

Очевидно, это не сработает; потому что aggregate возвращает словарь, а не QuerySet (см. docs), поэтому вы не можете связать два вызова aggregate вместе.

Я думаю, что использование annotate решит вашу проблему . annotate почти идентичен aggregate, за исключением того, что он возвращает QuerySet с результатами, сохраненными как атрибуты, а не возвращает словарь. В результате вы можете связать вызовы annotate или даже позвонить annotate, затем aggregate.

Так что я считаю что-то вроде:

return self.model.objects.filter(
    date__range=(start_date, end_date)
).annotate(  # call `annotate`
    sales=Sum(F("value")),
    purchase_cogs=Sum(F('purchase_cogs')),
    direct_cogs=Sum(F("direct_cogs")),
    profit=Sum(F('profit'))
).aggregate(  # then `aggregate`
    margin=Case(
        When(sales=0, then=0),
        default=(Sum(F('profit')) / Sum(F('value')))*100
    )
)

Должно сработать.

Надеюсь это поможет.

14
Saeed Arabi 7 Сен 2016 в 16:11

Вы не можете объединить такие агрегированные утверждения в цепочку. В документах говорится:

aggregate () - это терминальное предложение для QuerySet, которое при вызове возвращает словарь пар имя-значение.

Aggregate () - это терминальное предложение для QuerySet, которое при вызове возвращает словарь пар имя-значение.…

В отличие от aggregate (), annotate () не является терминальным предложением. Результатом предложения annotate () является QuerySet.

В отличие от aggregate (), annotate () не является терминальным предложением. Результатом предложения annotate () является QuerySet.…

1
denvaar 7 Сен 2016 в 16:15

Я заставил его работать (в Django 2.0) с помощью:

from django.db.models import Case, F, FloatField, Sum, When

aggr_results = models.Result.objects.aggregate(
    at_total_units=Sum(F("total_units")),
    ag_pct_units_sold=Case(
        When(at_total_units=0, then=0),
        default=Sum("sold_units") / (1.0 * Sum("total_units")) * 100,
        output_field=FloatField(),
    ),
)
2
msonsona 17 Окт 2018 в 14:18