Django 计算汇总总数

Django Calculate Aggregated Totals

我正在尝试构建一个 table 来根据用户 ID 汇总统计信息。这是第一次提出问题,如果我遗漏了任何重要问题,请多多包涵。

这是模型:

class Batting(models.Model):
  player = models.ForeignKey(
    'Player',
    on_delete=models.CASCADE,
    null=True,
    )
  game = models.ForeignKey(
    'Game',
    on_delete=models.CASCADE,
    null=True,
    blank=True,
    )
  season = models.ForeignKey(
    'Season', 
    on_delete=models.CASCADE,
    null=True,
    )
  games_played = models.IntegerField(
    blank=True,
    default=1,
    )
  plate_appearances = models.IntegerField(
    blank=True,
    default=0,
    )
  at_bats = models.DecimalField(
    default=0,
    max_digits=6,
    decimal_places=3,
    )
  hits = models.DecimalField(
    blank=True,
    max_digits=6,
    decimal_places=3,
    default=0,
    )
  ...
  batting_average = models.DecimalField(
    max_digits=6,
    decimal_places=3,
    editable=False,
    null=True
    )
  slugging_percentage = models.DecimalField(
    max_digits=6,
    decimal_places=3,
    editable=False,
    null=True
    )
  on_base_percentage = models.DecimalField(
    max_digits=6,
    decimal_places=3,
    editable=False,
    null=True
    )
  on_base_plus_slugging_percentage = models.DecimalField(
    max_digits=6,
    decimal_places=3,
    editable=False,
    null=True
    )

  def save(self, *args, **kwargs):
        self.total_bases = self.singles + (self.doubles * 2) + (self.triples * 3) + (self.home_runs * 4)
        self.extra_base_hits = self.doubles + self.triples + self.home_runs
        self.batting_average = float(self.hits) / float(self.at_bats)
        self.slugging_percentage = self.total_bases / float(self.at_bats)
        self.on_base_percentage = (self.hits + self.base_on_balls + self.hit_by_pitch) / float(self.at_bats + self.base_on_balls + self.sacrifice_flys + self.hit_by_pitch)
        self.on_base_plus_slugging_percentage = (self.hits + self.base_on_balls + self.hit_by_pitch) / float(self.at_bats + self.base_on_balls + self.sacrifice_flys + self.hit_by_pitch)
        super(Batting, self).save(*args, **kwargs)

  class Meta:
    verbose_name = u'Batting Stat'
    verbose_name_plural = u'Batting Stats'

  def __unicode__(self):
    return self.player.last_name

视图如下:

def stats(request):
  batting_stats = Batting.objects.values('player__id').annotate(
    fn=F('player__first_name'),
    ln=F('player__last_name'),
    total_hits=Sum(('hits'),output_field=FloatField()),
    total_at_bats=Sum(('at_bats'),output_field=FloatField()),
    avg=Avg('batting_average'),
    slg=Avg('slugging_percentage'),
    obp=Avg('on_base_percentage'),
    ops=Avg('on_base_plus_slugging_percentage'),
    )

  return render(request, 'stats.html', {
    'batting_stats': batting_stats,
    })

我遇到的问题是avg, slg, obp, ops都是模型中的计算,在View中Averaging them是求平均值,而不是计算总计,是根据player ID聚合的.

我试过使用聚合函数,但还没有找到让它工作的方法。有没有一种方法可以结合使用 Annotate 和 Aggregate,使我能够在单个玩家 ID 下整合统计数据并将这些相同的计算应用于总数?

如我所见,实际上您需要对单个击球实例和您的统计视图进行完全相同的计算。所以我将它放在一个函数中并从两点调用它:

def count_averages(data, result_recipient=None):
    result_recipient = data if result_recipient=None else result_recipient
    result_recipient.total_bases = data.singles + (data.doubles * 2) + (data.triples * 3) + (data.home_runs * 4)
    result_recipient.extra_base_hits = data.doubles + data.triples + data.home_runs
    ...

def save(self, *args, **kwargs):
    count_averages(self)
    super(Batting, self).save(*args, **kwargs)

def stats(request):
    sum_of_all_batting_stats = Batting.objects.filter('player__id'=player__id).annotate(
      sum('singles'),
      sum('doubles'),
      sum('triples'),
      ...
    )
    batting_stats = {}
    count_averages(self, batting_stats)

    return render(request, 'stats.html', {
        'batting_stats': batting_stats,
    })

这不是 100% 有效的代码,但您应该明白:您将计算移至分离的函数并调用 2 个不同的数据 - 1) 一个击球实例和 2) 所有击球的总和实例。

P.S。如果您每次都为每个视图调用计算它,那可能会导致性能问题。我建议将聚合统计信息存储在数据库 table 或某些缓存中。