Django:获取每组最新的N条记录

问题描述 投票:0回答:2

假设我有以下 Django 模型:

class Team(models.Model):
    name = models.CharField(max_length=255)
    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 中编写此查询?

django django-models django-queryset
2个回答
4
投票

不确定如何让每个团队获得重复的名称,因为您有

unique=True
。但是如果你打算删除它以支持非唯一名称,你可以使用这样的子查询:

top_3_per_team_name = Team.objects.filter(
    name=OuterRef("name")
).order_by("-created_at")[:3]

Team.objects.filter(
    id__in=Subquery(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 上支持这个所以理论上上面应该一旦发布就可以工作。


1
投票

我假设你会从

get
请求或其他东西中得到你的 N,但只要你有一个数字,你就可以尝试 limiting 你的查询集:

Team.objects.order_by("name", "-created_at").distinct("name")[:3] # for N = 3 
© www.soinside.com 2019 - 2024. All rights reserved.