根据同一模型上的子查询生成的行数更新 Django 模型

Rob*_*jic 7 python django postgresql django-models django-orm

我有一个 PostgreSQLUPDATE查询,它根据同一表中global_ranking按另一个字段 ( ) 排序的每行的字段 ( ) 更新表中每一行的字段 ( )。此外,更新是分区的,因此每行的排名仅相对于属于同一 的那些行。ROW_NUMBER()ratinglanguage

简而言之,我根据每个玩家当前的评分来更新游戏中每个玩家的排名。

PostgreSQL 查询如下所示:

UPDATE stats_userstats
SET    
    global_ranking = sub.row_number
FROM  (
    SELECT id, ROW_NUMBER() OVER (
        PARTITION BY language
        ORDER BY rating DESC
    ) AS row_number
    FROM stats_userstats
) sub
WHERE stats_userstats.id = sub.id;
Run Code Online (Sandbox Code Playgroud)

我也在使用 Django,如果可能的话,学习如何使用 Django ORM 表达此查询会很有趣。

起初,Django 似乎拥有表达查询所需的一切,包括使用 PostgreSQL 的ROW_NUMBER()窗口函数的能力,但我最好的尝试是使用以下命令更新所有ranking1

from django.db.models import F, OuterRef, Subquery
from django.db.models.expressions import Window
from django.db.models.functions import RowNumber

UserStats.objects.update(
    global_ranking=Subquery(
        UserStats.objects.filter(
            id=OuterRef('id')
        ).annotate(
            row_number=Window(
                expression=RowNumber(),
                partition_by=[F('language')],
                order_by=F('rating').desc()
            )
        ).values('row_number')
    )
)
Run Code Online (Sandbox Code Playgroud)

我曾经from django.db import connection; print(connection.queries)看过 Django ORM 语句生成的查询,并得到了这个模糊相似的 SQL 语句:

UPDATE "stats_userstats" 
SET "global_ranking" = (
    SELECT ROW_NUMBER() OVER (
        PARTITION BY U0."language"
        ORDER BY U0."rating" DESC
    ) AS "row_number" 
FROM "stats_userstats" U0 
WHERE U0."id" = "stats_userstats"."id"
Run Code Online (Sandbox Code Playgroud)

看起来我需要做的是将子查询从SET查询​​部分移动到FROM,但我不清楚如何重构 Django ORM 语句来实现这一点。

任何帮助是极大的赞赏。谢谢你!

Krz*_*arz 3

Subquery按提供的过滤 qs OuterRef1如果只考虑每个用户,您总是会得到任何排名中的第一名。

一个“正确”的查询是:

UserStats.objects.alias(
    row_number=Window(
        expression=RowNumber(),
        partition_by=[F('language')],
        order_by=F('rating').desc()
    )
).update(global_ranking=F('row_number'))
Run Code Online (Sandbox Code Playgroud)

但 Django 不允许这样做:

django.core.exceptions.FieldError: Window expressions are not allowed in this query

相关 Django 票证:https://code.djangoproject.com/ticket/25643

我想你可以在那里评论你的用例。