Django,添加来自反向外键查询的数据(带过滤器的外键联接)



我正在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_relatedselect_related只是优化方法,关系始终存在,这两种方法只是在一批中进行查询。

由于您最有可能循环使用queryset游戏,因此您可以执行以下操作:

games = Games.objects.with_player(user)
for game in games:
    players = game.players.values_list('player__first_name',
                                       'player__last_name')

这样做的缺点是,您可以为player指定想要显示的任意多个字段,但它不会给您一个完整的播放器对象。我认为在GamePlayer之间创建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)

通过这种方式,我在结果中获得了一个具有反向外键关系的"字段",并且我也能够对其进行过滤。

不确定是否有好的方法来避免这两个步骤,但这对我来说很好。

相关内容

  • 没有找到相关文章

最新更新