假设我有以下 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 中编写此查询?
不确定如何让每个团队获得重复的名称,因为您有
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 上支持这个所以理论上上面应该一旦发布就可以工作。
我假设你会从
get
请求或其他东西中得到你的 N,但只要你有一个数字,你就可以尝试 limiting 你的查询集:
Team.objects.order_by("name", "-created_at").distinct("name")[:3] # for N = 3