关于模型Django方式的最后'n'ForeignKey的微积分(注释/聚合)?

时间:2016-02-19 19:40:49

标签: django django-models django-rest-framework django-serializer

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

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

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)

我需要为“播放器详细信息”视图显示以下值:

  • 过去5场比赛的球员的平均速度得分(会话= FieldPlayers实例)
  • 过去5场比赛的玩家的平均stamina_score。
  • 过去5场比赛中球员的平均活动得分。
  • 得分值=以上3项得分的平均值。
  • 过去5个会话的数组中的所有'report_id'值。

事实上,我已经能够以编程方式计算所有这些。例如,要获取所有最近的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方式实现这种计算?这甚至是可能的和/或良好的做法吗?

我是Django和Python的新手。

2 个答案:

答案 0 :(得分:2)

如果您要在模型上查找这些值作为属性或函数,那么:

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中的.aggregate装饰器herevalues_list以及docs中的(def v (with-meta [0 1 2 3] {:foo :bar})) ;; => [0 1 2 3] (def sv (subvec v 0 2)) ;; => [0 1] (meta sv) ;; => nil ; where did the metadata? 。< / p>

编辑:好的,从评论中的澄清更新:)

答案 1 :(得分:1)

您可以在单个(!)数据库查询中轻松检索前四个点中描述的信息。只需将查询集限制为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值的列表。