Django中的自引用聚合

时间:2018-06-24 00:34:34

标签: django django-models

是否可以在Django中编写自引用聚合?

例如,给定以下模型定义:

from django.db import models

class Match(models.Model):
    match_id = models.BigIntegerField(primary_key=True)
    start_time = models.DateTimeField()
    league = models.ForeignKey(League, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete.models.CASCADE)

我想为每个Match对象添加先前匹配的数量。 (并最终在其他条件下,例如,与给定球队比赛的先前比赛。)

我最初的想法是:

from django.db.models import Q, F, Count

matches = Match.objects.filter(team__name='xyz')

matches.annotate(
    prior_matches=Count('match_id', filter=(
        Q(start_time__lt=F('start_time')
    ))
)

不幸的是,这似乎为所有对象提供了prior_matches=0

This question建议遵循一个外键并使用反向关系,但这似乎很笨拙,而且还存在两个问题:

  1. 这不是严格等同的。例如,以下代码段有效,但将计算给定联赛中而不是整体中的先前比赛。我猜您可以通过创建一个占位符模型来解决此问题,该占位符模型将所有Match对象都用作外键,但这似乎并不理想。

matches.annotate(
     prior_matches=Sum(Case(
         When(start_time__lt=F('league__matches__start_time'), then=1),
         default=0,
         output_field=models.IntegerField()
     ))
)
  1. 提供的计数是相对于数据库中所有以前的匹配的,而不仅仅是相对于已过滤查询集中的那些。例如,即使matches查询集是为特定团队过滤的,上面的代码也会找到所有先前匹配的计数。我想不出一个好办法(除了重复在每个Q / filter子句中应用于查询集的完整过滤条件,这在管理器中是不通用的)。

为了避免xy问题,我想通过在数据库级别工作来替换的(伪)代码片段之一如下:

def generate_stats(match):
    matches = Match.objects.filter(start_time__lt=match.start_time)

    for team in (match.home_team, match.away_team):
        q_involving = Q(home_team=team) | Q(away_team=team)
        team_matches = matches.filter(q_involving)

        team_matches.aggregate(...)  # get stats

1 个答案:

答案 0 :(得分:0)

我认为您应该将第一个代码更改为

from django.db import models

class Match(models.Model):
    match_id = models.AutoField(primary_key=True)
    start_time = models.DateTimeField()
    league = models.ForeignKey(League, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete.models.CASCADE)

否则,可能是您实际上没有添加“匹配”

相关问题