我正在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)))
但是运气不好。并对CCD_ 2和CCD_。做这件事的正确方法是什么?
fetch_related
和select_related
只是优化方法,关系始终存在,这两种方法只是在一批中进行查询。
由于您最有可能循环使用queryset游戏,因此您可以执行以下操作:
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()
Django文档中关于m2m的内容。
注意:through
主要用于m2m关系具有额外属性的情况。但是,如果你的代码中没有任何类似的东西(也许你在简化,但只是为了以防万一),你可以使用ManyToManyField
并去掉PlayerInGame
模型,django无论如何都会为你创建中间数据库表。
编辑:
在模板中你做:
{% 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)
通过这种方式,我在结果中获得了一个具有反向外键关系的"字段",并且我也能够对其进行过滤。
不确定是否有好的方法来避免这两个步骤,但这对我来说很好。