模型 Django 方式的最后 'n' 外键微积分 (annotate/aggregate)?

Calculus on last 'n' ForeignKey of a Model Django way (annotate/aggregate)?

这是我的问题:我有两个模型:FieldPlayer(它代表具有不同值的运动会话,例如 report_id、得分、平均速度等)和 Player(可以执行多个会话的玩家,他有了名字、球队和联赛)。

以下是我的代码中的这些模型:

class FieldPlayer(models.Model):
  """                                                                                                                                                                                                                                          
  Represent the performance of a player on a game.                                                                                                                                                                                             
  Has an owner and a remote report url.                                                                                                                                                                                                        
  """
  owner = models.ForeignKey(Player, related_name='performances')
  report_id = models.IntegerField(null=True)
  date = models.DateTimeField(default=timezone.now)
  distance = models.FloatField(default=0)
  pace = models.FloatField(default=0)
  training_length = models.FloatField(default=0)
  running_time_ratio = models.FloatField(default=0)
  h_i_average_speed = models.FloatField(default=0)
  h_i_run_time = models.FloatField(default=0)
  explosivity = models.FloatField(default=0)
  run_number = models.IntegerField(default=0)
  overview = JSONField(null=True, blank=True) # Deprecated                                                                                                                                                                                     
  game = models.ForeignKey(
    Game, null=True, blank=True, related_name='players')
  was_home = models.BooleanField(default=True)
  speed_score = models.IntegerField(default=0)
  stamina_score = models.IntegerField(default=0)
  activity_score = models.IntegerField(default=0)

我的播放器模型:

class Player(models.Model):
  name = models.CharField(max_length=50)
  picture = ThumbnailerImageField(
    upload_to='profile_pictures', resize_source={'size': (200, 200),
  'crop': 'smart'}, blank=True, null=True)
  team = models.ForeignKey(
    Team, null=True, blank=True, related_name='players')
  # TODO: Replace with db model instead of static string                                                                                                                                                                                       
  league = {
    "id": 1,
    "name": "Champigny - Elite",
    "logo": None
  }
  average_speed_score = models.IntegerField(default=0)
  average_stamina_score = models.IntegerField(default=0)
  average_activity_score = models.IntegerField(default=0)
  score = models.IntegerField(default=0)

我需要获取以下值以显示我的 "Player-detail" 视图:

事实上,我已经能够以编程方式计算所有这些。例如,为了获得所有最近的 reports_ids 我在我的玩家模型中做了这些方法:

def get_n_last_sessions(self, n):
  return self.performances.all()[:n]

def recents_reports(self):
  sessions_list = self.get_n_last_sessions(5)
  reports_ids = []
  for sessions in  sessions_list:
    reports_ids.append(sessions.report_id)
  return reports_ids

我可以为我需要的所有计算做同样的事情。但这是实现这一目标的好方法吗?我不这么认为,但如果我错了,请告诉我。

所以这才是真正的问题:如何用 Django annotateaggregate 方式实现这种计算?这甚至可能 and/or 好的做法吗?

我完全不熟悉 Django 和 Python。

如果您正在寻找这些值作为模型的属性或函数,那么:

from django.db.models import Avg


class Player(models.Model):
    ....

    ....
    @property
    def average_speed_score(self):
        return self.performances.order_by('-date')[:5].aggregate(Avg('speed_score'))

    @property
    def average_stamina_score(self):
        return self.performances.order_by('-date')[:5].aggregate(Avg('stamina_score'))

    @property
    def average_activity_score(self):
        return self.performances.order_by('-date')[:5].aggregate(Avg('activity_score'))

    @property
    def score(self):
        return sum([self.average_speed_score, average_stamina_score, self.average_activity_score])/3

    def return_last_n_report_ids(self, n):
        return self.performances.objects.order_by('-date')[:n].values_list('report_id', flat=True)

您还可以了解 @property 装饰器 here, .aggregate in the docs, and values_list in the docs

编辑:好的,根据评论中的说明更新:)

您可以在单个 (!) 数据库查询中轻松检索前四个要点中描述的信息。只需将查询集限制为 5 个元素 ([:5]) 并执行适当的聚合:

from django.db.models import Avg

FieldPlayer.objects.filter(player=player).order_by('-date')[:5].aggregate(
    avg_speed_score=Avg('speed_score'),
    avg_stamina_score=Avg('stamina_score'),
    avg_activity_score=Avg('activity_score'),
    avg_all=(Avg('speed_score')+Avg('stamina_score')+Avg('activity_score'))/3,
)

player 是当前 Player 对象。

最后一点有点不同。这不是像上面那样的聚合,所以我认为你必须为此执行第二个查询:

FieldPlayer.objects.filter(player=player) \
    .order_by('-pk')[:5].values_list('report_id', flat=True)

这将为您提供最近 5 个会话的所有 report_id 值的列表。