首页 > 解决方案 > 在 Django 查询中计算多次出现

问题描述

我有两个模型:球员和锦标赛。

玩家也有他们玩的游戏类型(dota、lol、魔术等)。他们可以同时参加多场比赛(每场比赛只能参加一次)。为了管理铭文,我使用了另一个名为 TournamentMatch 的模型,它为每个铭文创建一个新对象,其中包含锦标赛和玩家的 ID。

class Player(models.Model):
    name = models.CharField(_('name'), max_length=150, null=True, blank=True)
    email = models.EmailField(_('email address'), unique=True)
    is_dota_player = models.BooleanField(default=False)
    is_lol_player = models.BooleanField(default=False)
    is_magic_player = models.BooleanField(default=False)

class Tournament(models.Model):
    name = models.CharField(max_length=200)
    date_start = models.DateField()
    date_end = models.DateField()

class TournamentMatch(models.Model):
    tournament = models.ForeignKey(Tournament)
    player = models.ForeignKey(Player)
    date_added = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

我想计算拥有两个以上锦标赛铭文的玩家数量,例如,dota 玩家。

我可以很容易地为每个玩家使用 for 循环来实现这一点,但出于性能原因,我想使用 Django 查询来实现这一点。

例如,如果我想统计拥有一个或多个铭文的 DOTA 玩家,我会这样做:

TournamentMatch.objects.filter(
    player__is_dota_player=True
).distinct(
   'player'
).count()

我确信这是可能的,但我不知道如何计算锦标赛中不同玩家的每一次出现,并且只过滤那些拥有多个(而不仅仅是一个)的玩家。

我将不胜感激任何帮助或指示!

标签: django

解决方案


# subquery for counting match per player (note OuterRef which will be linked to the outer query row)
player_match_count_subquery = TournamentMatch.objects.filter(player_id=OuterRef('pk'), ).\
    annotate(match_count=Count('pk'), ).values('match_count', )[:1]

# the main (outer) query which uses subquery output for filtering
Players.objects.filter(is_dota_player=True, ).\
    annotate(match_count=Subquery(player_match_count_subquery), ).\
    filter(match_count__gt=2, )

https://docs.djangoproject.com/en/2.2/ref/models/expressions/#subquery-expressions

此代码将在逻辑上生成此 SQL 查询:

SELECT p.*, tm.match_count
FROM players p
CROSS APPLY (
    SELECT COUNT(id) match_count
    FROM tournamentmatch tm
    WHERE tm.player_id = p.id
) tm
WHERE p.is_dota_player = $true
    AND tm.match_count > 2

推荐阅读