Django ORM:基于关联模型数量创建排名

3
我在数据库中有两个模型:电影评论(它具有指向电影的外键)。
from django.db import models


class Movie(models.Model):
    title = models.CharField(max_length=120, blank=False, unique=True)

class Comment(models.Model):
    movie = models.ForeignKey(
        movie_models.Movie, null=False, on_delete=models.CASCADE
    )
    body = models.TextField(blank=True)

我的目标是创建一个基于相关评论数确定排名的电影排行榜。

我可以注释评论数并正确排序结果:

from django.db.models import Count

qs = Movie.objects.annotate(
    total_comments=Count('comment')
).order_by('-total_comments')

这给了我正确排序的查询集,但我想做更多的事情 - 为每一行注释“rank”。
那么我的问题是:如何将“rank”注释到结果的每一行?请注意,需要对具有相同评论数量的电影具有相同的排名。
|  movie_title | total_comments | rank |
|--------------|----------------|------|
| mov1         | 10             | 1    |
| mov2         | 5              | 2    |
| mov3         | 5              | 2    |
| mov4         | 3              | 3    |

我尝试使用窗口函数,因为其中一些例子对我来说似乎是合法的:

from django.db.models import F, Window

dense_rank = Window(
    expression=DenseRank(),
    order_by=F('total_comments').desc(),
)

qs = Movie.objects.annotate(
    total_comments=Count('comment')
).annotate(rank=dense_rank)

但是运行此查询会引发 django.db.utils.OperationalError: near "(": syntax error 错误


请注意,由于这仅用于显示目的,因此您也可以在Python中计算排名。 - Kevin Christopher Henry
如果数据库查询无法得到预期结果,我会这样做。 - umat
你想出可行的解决方案了吗? - Marek
这个问题的发生是由于使用了SQLite数据库后端所引起的。当我切换到Postgres时,代码开始按预期工作。 - umat
1个回答

4

如果查询已适当排序,则RowNumber应该足够:

from django.db.models import Count
from django.db.models.expressions import F, Window
from django.db.models.functions.window import RowNumber

qs = (Movie.objects
    .annotate(total_comments=Count('comment'))
    .order_by('total_comments')
    .annotate(rank = Window(expression=RowNumber())
)

但是由于行号在记录序列中固有,因此Kevin建议的一种简单的Python方法(例如enumerate)是正确的,因为你在某个时候需要迭代记录集。

如果您想要不同的记录集排序而非排名,则使用带有单独的order_by参数的Window会有意义。

我不确定所有后端是否都支持RowNumber


在提问之前,我一直在使用sqlite后端。现在我还在Postgres上进行了测试,原来的问题中的查询按预期工作!这很奇怪,因为似乎sqlite应该支持窗口函数。 - umat
2
我的查询似乎只在 PG 上有效,而不是 SQLite,即使它在 SQLite shell 中执行得很好。窗口函数是相当新的,可能尚未完全工作。 - Endre Both

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接