13

我正在尝试找到一种有效的方法来查找数据库中与其分数相关的对象的排名。我天真的解决方案如下所示:

rank = 0
for q in Model.objects.all().order_by('score'):
  if q.name == 'searching_for_this'
    return rank
  rank += 1

应该可以使用 order_by 让数据库进行过滤:

Model.objects.all().order_by('score').filter(name='searching_for_this')

但是似乎没有办法在过滤器之后检索 order_by 步骤的索引。

有一个更好的方法吗?(使用 python/django 和/或原始 SQL。)

我的下一个想法是在插入时预先计算排名,但这似乎很混乱。

4

4 回答 4

17

我认为您不能使用 Django ORM 在一个数据库查询中执行此操作。但如果它不打扰你,我会在模型上创建一个自定义方法:

from django.db.models import Count

class Model(models.Model):
    score = models.IntegerField()
    ...

    def ranking(self):
        count = Model.objects.filter(score__lt=self.score).count()
        return count + 1

然后,您可以在任何地方使用“排名”,就好像它是一个普通字段一样:

print Model.objects.get(pk=1).ranking

编辑:这个答案来自 2010 年。现在我会推荐Carl 的解决方案

于 2010-04-17T17:24:00.927 回答
13

在 Django 2.0 中使用新的Window 函数,你可以这样写...

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

Model.objects.filter(name='searching_for_this').annotate(
            rank=Window(
                expression=Rank(),
                order_by=F('score').desc()
            ),
        )
于 2018-07-13T03:33:18.767 回答
3

使用这样的东西:

obj = Model.objects.get(name='searching_for_this')
rank = Model.objects.filter(score__gt=obj.score).count()

如果经常使用并影响性能,您可以预先计算排名并将其保存到模型中。

于 2010-04-17T17:20:48.173 回答
2

在具有符合标准的数据库引擎(PostgreSql、SQL Server、Oracle、DB2 ......)的“原始 SQL”中,您可以只使用 SQL 标准RANK函数——但这在流行但非标准的引擎中不受支持,例如作为 MySql 和 Sqlite,并且(也许正因为如此)Django 不会将这个功能“呈现”给应用程序。

于 2010-04-17T17:20:39.167 回答