这是我的问题:我有两个模型: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)
我需要为“播放器详细信息”视图显示以下值:
事实上,我已经能够以编程方式计算所有这些。例如,要获取所有最近的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 annotate
或aggregate
方式实现这种计算?这甚至是可能的和/或良好的做法吗?
我是Django和Python的新手。
答案 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
装饰器here,values_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
值的列表。