Django ORM: как сделать агрегацию с фильтром с аннотированным полем? - PullRequest
0 голосов
/ 23 сентября 2019

У меня есть представление со статистикой, где я вычисляю несколько count на разных фильтрах некоторого базового набора запросов:

qs = Model.onjects.filter(...).annotate(a=...)
a = qs.filter(Q(a__lt=5)).count()
b = qs.filter(Q(a__lt=10)).count()  # this is just an example, real filters are more complex
...

Но каждый счет делает отдельный запрос к БД, и я хочу оптимизировать его,Я пытался aggregation:

qs.aggregate(
  a=Count('a', filter=Q(a__lt=5)), 
  b=Count('a', filter=Q(a__lt=10)),
)

, но получил ошибку: django.db.utils.OperationalError: (1054, "Unknown column '__col2' in 'field list'").Я даже не знаю, откуда взялась эта __col2.

Кажется, что агрегация плохо работает с аннотацией, потому что когда я использую обычное поле модели внутри count.filter вместо аннотированного поля a всегов порядке.

1 Ответ

0 голосов
/ 23 сентября 2019

Если вы используете Django 2.2, ваш подход должен работать, как объяснено здесь .Вы, вероятно, должны рассчитывать на 'pk', как показано там, но не уверены, что можете рассчитывать на саму аннотацию:

qs.aggregate(
    a=Count('pk', filter(Q(a__lt=5)),
    b=Count('pk', filter(Q(a__lt=10))
)

Если вы используете Django 1.11, вышеупомянутый подход не работает как Countвсегда будет возвращать 1, игнорируя фильтр.Вы должны использовать Case ... When:

qs.aggregate(
    a=Sum(
        Case(When(a__lt=5, then=1),
             output_field=IntegerField())
    ),
    b=Sum(
        Case(When(a__lt=10, then=1),
             output_field=IntegerField())
    )
)
...