从Django RelatedManager的中间模型注解字段

rqqzpn5f  于 12个月前  发布在  Go
关注(0)|答案(1)|浏览(124)

给定一个Django的ManyToManyField中间模型,例如:

class Player(models.Model):
    name = models.CharField(max_length=128)

class Team(models.Model):
    name = models.CharField(max_length=128)
    players = models.ManyToManyField(
        Player, through='Membership', related_name='teams'
    )

class Membership(models.Model):
    player = models.ForeignKey(Player, related_name='membership')
    team = models.ForeignKey(Team, related_name='membership')
    is_manager = models.BooleanField()

我想将Membership中的字段注解到相关的管理器查询中,例如:

team = get_team()

team.players.annotate(
    is_manager=F('membership__is_manager')
).all()

但这会导致memberships返回到players的外部连接。
我知道我可以改变查询方式,直接查询Membership,但我需要的是Player对象,以提供给序列化器。很明显我会用SQL来表达它,但我不能用ORM来表达它。

**更新:**我已经想出了一个绝对可怕的解决方法来处理外部连接,那就是再次过滤,但必须有一个更好的方法:

team.players.annotate(
    filter_team_id=F('membership__team_id'),
    is_manager=F('membership__is_manager'),
).filter(
    filter_team_id=team.id
)
kulphzqa

kulphzqa1#

.但这会导致成员资格返回到玩家的外部联接。
在Django 3.2上,原始查询看起来并没有那么糟糕。至少它 * 不 * 包含 * 外部连接 *,据我所知:

print(team.players.annotate(is_manager=F('membership__is_manager')).query)

会产生类似于

SELECT "player"."id", "membership"."is_manager" AS "is_manager" 
FROM "player" 
INNER JOIN "membership" ON ("player"."id" = "membership"."player_id") 
WHERE "membership"."team_id" = 1

(为清晰起见,缩短了表名)
如果更频繁地需要注解,则可以添加自定义管理器方法,尽管这不会修改实际的查询。举例来说:

class PlayerManager(models.Manager):
    def annotated(self):
        return self.annotate(is_manager=models.F('membership__is_manager'))

class Player(models.Model):
    ...
    objects = PlayerManager()

# then you can do...
team.players.annotated()

相关问题