【问题标题】:Django: Get latest N number of records per groupDjango:获取每组最新的N条记录
【发布时间】:2022-12-07 07:37:14
【问题描述】:

假设我有以下 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 中编写此查询?

【问题讨论】:

  • 如果我理解正确的话:Team.objects.filter(name='super_team').count()
  • 不,我不想数数。我想要实际的 N 条最新记录。
  • 你关心速度吗?
  • 请试一试Team.objects.all().values('name').annotate(total_n=Count('name')).order_by('total_n')
  • @TarasMykhalchuk 我希望它快点。不幸的是,您的解决方案没有奏效。

标签: django django-models django-queryset


【解决方案1】:

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

Team.objects.order_by("name", "-created_at").distinct("name")[:3] # for N = 3 

【讨论】:

  • 不幸的是,这不起作用
【解决方案2】:

不确定如何获得每个团队的重复名称,因为您有 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 上支持这个所以理论上上面应该在发布后工作。

【讨论】:

    猜你喜欢
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2013-12-31
    • 1970-01-01
    • 2021-03-27
    • 2021-09-01
    相关资源
    最近更新 更多