Django,从反向外键查询添加数据(外键与过滤器连接)
Django, add data from reverse Foreign key query (foreign key join with filter)
我正在用 Django 编写一个小型网页游戏。每场比赛有两名球员。为了管理游戏和参与者,我有这样的东西(非常简单):
class GamesQuerySet(models.query.QuerySet):
def with_player(self, user):
"""Games with |user| participating"""
return self.filter(players__player=user)
class Game(models.Model):
"""All the games"""
name = models.CharField(max_length=100, blank=True)
objects = GamesQuerySet.as_manager()
class PlayerInGame(models.Model):
"""Players participating in games"""
game = models.ForeignKey('Game', related_name='players')
player = models.ForeignKey('auth.User')
class Meta:
unique_together = ('game', 'player')
所以我可以轻松查询当前玩家正在参加的游戏:
Games.objects.with_player(user)
但是为了在游戏视图列表中显示,我还想包括其他玩家的名字。
我尝试了一些方法,似乎可以通过添加 .extra()
和一些原始的 SQL 来实现,但我想尽量避免这种情况可能。
我试过使用这样的注释:
def add_other_player(self, user):
return self.annotate(other_player=PlayerInGame.objects.all(). \
filter(~Q(player=user)))
但运气不佳。还研究了 fetch_related
和 select_related
。这样做的正确方法是什么?
fetch_related
和select_related
只是优化方法,关系一直存在,这2种方法只是做一批查询。
由于您很可能要遍历查询集游戏,您可以这样做:
games = Games.objects.with_player(user)
for game in games:
players = game.players.values_list('player__first_name',
'player__last_name')
这样做的缺点是您可以为 player
指定要显示的任意多个字段,但它不会为您提供完整的播放器对象。我认为在 Game
和 Player
之间创建 ManyToMany 关系实际上更有意义,因为听起来就像你正在做的那样,PlayerInGame
模型作为 through
模型:
class PlayerInGame(models.Model):
"""Players participating in games"""
game = models.ForeignKey('Game', related_name='players')
player = models.ForeignKey('auth.User')
class Meta:
unique_together = ('game', 'player')
class Game(models.Model):
"""All the games"""
name = models.CharField(max_length=100, blank=True)
players = models.ManyToManyField('auth.User', through='PlayerInGame')
objects = GamesQuerySet.as_manager()
然后要获得游戏的玩家,您可以这样做:
players = game.players.all()
关于 m2m with through.
的 Django 文档
注意:through
主要用于 m2m 关系的额外属性。但是如果你的代码中没有任何额外的东西(也许你正在简化但以防万一它完全像那样),你可以只使用 ManyToManyField
并一起摆脱 PlayerInGame
模型, django 无论如何都会为你创建中间数据库 table。
编辑:
在模板中你做的:
{% for game in games %}
Current game is: {{ game.name }}
The players in the game are:
<ul>
{% for player in game.players.all %}
<li>{{ player.player.first_name }} {{ player.player.last_name }}</li>
{% endfor %}
</ul>
{% endfor %}
好的,我想我找到了我要找的东西。这是我所做的(分两步):
from django.db.models import F
game_ids = Games.objects.with_player(user).values_list('id', flat=True)
with_opponents = Games.objects.filter(id__in=game_ids) \
.annotate(opponent=F('players__player__username')) \
.filter(~Q(opponent=user.username)
这样,我在具有反向外键关系的结果中得到一个 "field",我也可以对其进行过滤。
不确定是否有避免这两个步骤的好方法,但这对我来说很好。
我正在用 Django 编写一个小型网页游戏。每场比赛有两名球员。为了管理游戏和参与者,我有这样的东西(非常简单):
class GamesQuerySet(models.query.QuerySet):
def with_player(self, user):
"""Games with |user| participating"""
return self.filter(players__player=user)
class Game(models.Model):
"""All the games"""
name = models.CharField(max_length=100, blank=True)
objects = GamesQuerySet.as_manager()
class PlayerInGame(models.Model):
"""Players participating in games"""
game = models.ForeignKey('Game', related_name='players')
player = models.ForeignKey('auth.User')
class Meta:
unique_together = ('game', 'player')
所以我可以轻松查询当前玩家正在参加的游戏:
Games.objects.with_player(user)
但是为了在游戏视图列表中显示,我还想包括其他玩家的名字。
我尝试了一些方法,似乎可以通过添加 .extra()
和一些原始的 SQL 来实现,但我想尽量避免这种情况可能。
我试过使用这样的注释:
def add_other_player(self, user):
return self.annotate(other_player=PlayerInGame.objects.all(). \
filter(~Q(player=user)))
但运气不佳。还研究了 fetch_related
和 select_related
。这样做的正确方法是什么?
fetch_related
和select_related
只是优化方法,关系一直存在,这2种方法只是做一批查询。
由于您很可能要遍历查询集游戏,您可以这样做:
games = Games.objects.with_player(user)
for game in games:
players = game.players.values_list('player__first_name',
'player__last_name')
这样做的缺点是您可以为 player
指定要显示的任意多个字段,但它不会为您提供完整的播放器对象。我认为在 Game
和 Player
之间创建 ManyToMany 关系实际上更有意义,因为听起来就像你正在做的那样,PlayerInGame
模型作为 through
模型:
class PlayerInGame(models.Model):
"""Players participating in games"""
game = models.ForeignKey('Game', related_name='players')
player = models.ForeignKey('auth.User')
class Meta:
unique_together = ('game', 'player')
class Game(models.Model):
"""All the games"""
name = models.CharField(max_length=100, blank=True)
players = models.ManyToManyField('auth.User', through='PlayerInGame')
objects = GamesQuerySet.as_manager()
然后要获得游戏的玩家,您可以这样做:
players = game.players.all()
关于 m2m with through.
的 Django 文档注意:through
主要用于 m2m 关系的额外属性。但是如果你的代码中没有任何额外的东西(也许你正在简化但以防万一它完全像那样),你可以只使用 ManyToManyField
并一起摆脱 PlayerInGame
模型, django 无论如何都会为你创建中间数据库 table。
编辑:
在模板中你做的:
{% for game in games %}
Current game is: {{ game.name }}
The players in the game are:
<ul>
{% for player in game.players.all %}
<li>{{ player.player.first_name }} {{ player.player.last_name }}</li>
{% endfor %}
</ul>
{% endfor %}
好的,我想我找到了我要找的东西。这是我所做的(分两步):
from django.db.models import F
game_ids = Games.objects.with_player(user).values_list('id', flat=True)
with_opponents = Games.objects.filter(id__in=game_ids) \
.annotate(opponent=F('players__player__username')) \
.filter(~Q(opponent=user.username)
这样,我在具有反向外键关系的结果中得到一个 "field",我也可以对其进行过滤。
不确定是否有避免这两个步骤的好方法,但这对我来说很好。