You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于中间表字段对Django多对多关系进行排序?

Django多对多关联未按中间表排序问题解决

问题核心

你在中间表ELO的Meta中设置了ordering = ("-current",),但通过GameMode.leaderboard查询关联的Player时,并未按该规则排序,导致测试断言失败。

原因

Django的多对多关联管理器(ManyRelatedManager)不会自动继承中间表的ordering配置。中间表的Meta.ordering仅在直接查询ELO对象(如ELO.objects.all())时生效;通过多对多字段查询关联模型(如game_mode.leaderboard)时,Django默认按关联模型(这里是Player)的主键排序,不会应用中间表的排序规则。

解决方案

方案1:在多对多字段定义时显式指定排序(推荐,全局生效)

修改GameMode模型的leaderboard字段,添加ordering参数关联中间表的current字段:

from django.db import models
import uuid

class Player(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, unique=True, editable=False)
    name = models.CharField(max_length=255)

class GameMode(models.Model):
    name = models.CharField(max_length=30)
    leaderboard = models.ManyToManyField(
        Player,
        through="ELO",
        related_name="game_modes",
        # 按中间表ELO的current字段降序排序
        ordering=("-elos__current",)
    )

class ELO(models.Model):
    player = models.ForeignKey(Player, related_name="elos", on_delete=models.PROTECT)
    game_mode = models.ForeignKey(GameMode, related_name="elos", on_delete=models.PROTECT)
    current = models.PositiveIntegerField(default=0)
    peak = models.PositiveIntegerField(default=0)

    class Meta:
        unique_together = ["player", "game_mode"]
        ordering = ("-current",)

注:这里通过-elos__current关联,是因为Player模型中ELO的related_name为elos。

方案2:查询时手动添加排序(局部生效)

如果不想修改模型,可在测试或业务代码中显式调用order_by:

def test_leaderboard_works_okay_with_different_game_modes(db):
    game_mode1 = GameMode.objects.create(name="mode1")
    game_mode2 = GameMode.objects.create(name="mode2")
    player1 = Player.objects.create(name="player1")
    player2 = Player.objects.create(name="player2")

    ELO.objects.create(game_mode=game_mode1, player=player1, current=1500)
    ELO.objects.create(game_mode=game_mode2, player=player1, current=1000)
    ELO.objects.create(game_mode=game_mode1, player=player2, current=1000)
    ELO.objects.create(game_mode=game_mode2, player=player2, current=1500)

    # 查询时显式按中间表current降序排序
    assert game_mode1.leaderboard.order_by("-elos__current").first() == player1
    assert game_mode2.leaderboard.order_by("-elos__current").first() == player2

验证修改

修改后重新运行测试,即可看到game_mode1.leaderboard默认返回按current降序排列的Player,断言会通过。

内容的提问来源于stack exchange,提问作者halkony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:16:08