Django: Получение последнего N количества записей для каждой группы
Допустим, у меня есть следующая модель Django:
class Team(models.Model):
name = models.CharField(max_length=255, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
Я хочу написать запрос для получения последнего N количества записей по названию команды.
Если N=1, то запрос очень прост (предполагая, что я использую postgres, потому что это единственная БД, которая поддерживает distinct(*fields)
):
Team.objects.order_by("name", "-created_at").distinct("name")
Если N больше 1 (допустим, 3), то все становится сложнее. Как я могу написать этот запрос в Django?
Я предполагаю, что вы будете получать N из запроса get
или чего-то еще, но пока у вас есть число, вы можете попробовать ограничить ваш набор запросов:
Team.objects.order_by("name", "-created_at").distinct("name")[:3] # for N = 3
Не уверен, как вы можете получить дубликаты имен на команду, поскольку у вас есть unique=True
. Но если вы планируете убрать это для поддержки неуникальных имен, вы можете использовать подзапросы, например, такой:
top_3_per_team_name = Team.objects.filter(
name=OuterRef("name")
).order_by("-created_at")[:3]
Team.objects.filter(
id__in=top_3_per_team_name.values("id")
)
Хотя это может быть немного медленно, поэтому убедитесь, что у вас настроены индексы.
Также следует отметить, что в идеале это можно решить с помощью Window
...[Django-doc] функций, использующих DenseRank
...[Django-doc], но, к сожалению, последняя версия django не может фильтровать на windows:
from django.db.models import F
from django.db.models.expressions import Window
from django.db.models.functions import DenseRank
Team.objects.annotate(
rank=Window(
expression=DenseRank(),
partition_by=[F('name'),],
order_by=F('created_at').desc()
),
).filter(rank__in=[range(1,4)]) # 4 is N + 1 if N = 3
С учетом вышесказанного вы получаете:
NotSupportedError: Window is disallowed in the filter clause.
Но есть план по поддержке этого на Django 4.2, так что теоретически вышеописанное должно работать после его выхода.