Django ORM: filter после подзапроса annotate

Рейтинг: 1Ответов: 1Опубликовано: 28.04.2023

Этот Джанго-ОРМ запрос:

Model.objects.all() \
    .annotate(
        ord=Window(
            expression=RowNumber(),
            partition_by=F('related_id'),
            order_by=[F("date_created").desc()]
        )
    ) \
    .filter(ord=1) \
    .filter(date_created__lte=some_datetime)

Приводит к такому SQL запросу:

SELECT * 
FROM (
    SELECT 
      id, related_id, values, date_created
      ROW_NUMBER() OVER (
        PARTITION BY related_id
        ORDER BY date_created DESC
      ) AS ord
    FROM model_table
    WHERE date_created <= 2022-02-24 00:00:00+00:00
)
WHERE ord = 1

Фильтр с date_created__lte применяется на внутренний подзапрос. Можно ли как-то вытащить это условие наружу, чтобы оно было вместе/после ord=1?

Есть объект подзапросов Subquery, однако он применяется только в выражениях SELECT и WHERE (методы annotate и filter), а не FROM, в который мне нужно инкапсулировать подзапрос, чтобы применить условие фильтрации на результат подзапроса.

Ответы

▲ 1

Придумал вариант как получит нужные мне данные: применить фильтр, в данном случае date_created__lte, вне запроса с аннотацией.

sub = Model.objects.all() \
    .annotate(
        ord=Window(
            expression=RowNumber(),
            partition_by=F('related_id'),
            order_by=[F('date_created').desc()]
        )
    ) \
    .filter(ord=1)

Model.objects.all() \
    .filter(id__in=sub.values_list('id')) \
    .filter(date_created__lte=some_datetime)

Однако этот код плох с точки зрения производительности т.к приводит к HASH JOIN. Конечно можно было бы написать чистый SQL запрос и распарсить в Django ORM, но это как-то слишком монструозно для простого подзапроса... В общем, решение получше будет очень кстати 🙏