首页 > 解决方案 > 查询 Django 中所有类型的每个唯一类型的第 n 个最新条目

问题描述

我已经研究过这个问题,答案是关于获取每种类型的最新信息,例如这个主题

不同之处在于我想为所有n类型获取每种类型的最新项目。目前我得到所有项目,然后在 python 中找到第 n 个最近的条目,这非常慢。

例如

class CakeType(models.Model):
    name = models.CharField(max_length=40)

class CakeEntry(models.Model):
    cake_type = models.ForeignKey(CakeType, on_delete=models.CASCADE)    
    created = models.DateTimeField()

怎么会说CakeEntry所有不同/独特的 5 个最新CakeType的?

我将数据库从 MySQL 迁移到 PostgreSQL(做了大量工作),所以我可以使用 Postgres 的DISTINCT ON.

阅读文档后,我尝试了以下操作:

from django.db.models import F, Window
from django.db.models.functions import Rank

    cake_entries = (
        CakeEntry.objects.order_by(
            '-created'
        ).annotate(
            rank=Window(
                expression=Rank(),
                partition_by=[F('cake_type_id')],
                order_by=F('created').desc(),
            ),
        ).filter(rank__lt=5)
    )

但是如果django.db.utils.NotSupportedError: Window is disallowed in the filter clause.没有等级/分区,我不确定如何做到这一点。

标签: pythondjango

解决方案


由于目标冲突,我看不到使用 Window 的方法:

为了解决过滤器问题,必须创建一个子查询。问题是用作表达式(注释)的子查询应该返回 1 行,以便将特定排名绑定到特定外部引用,但 Window 需要所有相关行的范围。最好的说明如下:

# Pk 21 has rank 3
CakeEntry.objects.all().annotate(rank=rank).values("rank", "pk")[:3]
Out[11]: <QuerySet [{'pk': 2, 'rank': 1}, {'pk': 1, 'rank': 2}, {'pk': 21, 'rank': 3}]>
# Due to filter, pk 21 has rank 1, even though you seemingly filter after the annotation
CakeEntry.objects.all().annotate(rank=rank).values("rank", "pk").filter(pk=21)
Out[12]: <QuerySet [{'pk': 21, 'rank': 1}]>

我能想到的最接近的是:

class CakeEntryManager(models.Manager):
    def top5_per_type(self):
        cake_types = CakeType.objects.all()
        qs = self.get_queryset()
        results = qs.none()
        for ct in cake_types:
            results = results.union(qs.filter(cake_type=ct).order_by("-created")[:5])
        return results.order_by("cake_type", "-created")

推荐阅读