如何基于中间表字段对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
相关产品推荐
相关产品推荐

