Как вычислить кумулятивную сумму поля count в Django

У меня есть модель, которая регистрирует некое событие и дату, в которую оно происходит. Мне нужно вычислить: 1) количество событий для каждой даты и 2) кумулятивное количество событий за определенное время.

Моя модель выглядит примерно так:

class Event(models.Model):
  date = models.DateField()
  ...

Вычисление 1) довольно простое, но у меня возникают проблемы с вычислением кумулятивной суммы. Я пытался сделать что-то вроде этого:

query_set = Event.objects.values("date") \
                         .annotate(count=Count("date")) \
                         .annotate(cumcount=Window(Sum("count"), order_by="date"))              

Но я получаю эту ошибку:

Cannot compute Sum('count'): 'count' is an aggregate

Редактируйте: В идеале, я хотел бы иметь набор запросов, эквивалентный этому SQL-запросу:

SELECT date,
       COUNT(date) as count,
       SUM(COUNT(date)) OVER(ORDER BY date) acc_count 
FROM event_event
GROUP BY date

Удивительно часто можно встретить разработчиков, желающих преобразовать SQL-запрос в Django QuerySet.

В данном случае, поскольку ОП уже знает SQL, ОП лучше просто выполнить необработанный SQL запрос.

Есть разные способы сделать это, например выполнение пользовательского SQL напрямую.

from django.db import connection

def my_custom_sql(self):
    with connection.cursor() as cursor:
        cursor.execute("SELECT date, COUNT(date) as count, SUM(COUNT(date)) OVER(ORDER BY date) acc_count 
                        FROM event_event
                        GROUP BY date")

Затем вызовите cursor.fetchone() или cursor.fetchall(), чтобы вернуть результирующие строки.

В некоторых случаях выполнение агрегата агрегата не допустимо в SQL, независимо от того, используете вы ORM или нет, например: MAX(SUM(...)). В вашем случае вы можете сделать это с помощью необработанного запроса (как уже упоминалось в других ответах и в вашем запросе). Или используя ORM следующим образом:

subquery = (
    Event.objects.filter(date=OuterRef("date"))  # we need this for the join
    .values("date")  # this to create the group by
    .annotate(subcount=Count("date"))  # the aggregate function
)
Event.objects.values("date").annotate(count=Count("date")).annotate(
    sumcount=Window(Sum(subquery.values("subcount")), order_by="date")
    # above we can use the Sum with the subquery
    # we can also replace it for any aggregation functions that we want
).values("date", "count", "cumcount")

В результате будет сгенерирован следующий SQL:

SELECT
    "app_event"."date",
    COUNT("app_event"."date") AS "count",
    SUM((SELECT
            COUNT(U0."date") AS "subcount" 
        FROM
            "app_event" U0 
        WHERE
            U0."date" = ("app_event"."date") 
        GROUP BY
            U0."date"
    )) OVER ( ORDER BY "app_event"."date")
    AS "cumcount" 
FROM
    "app_event" 
GROUP BY
    "app_event"."date"
Вернуться на верх