Django:计算来自同一模型的行上字段之间的差异

时间:2019-05-13 21:19:01

标签: django django-queryset

我有一个模型,它代表一项运动开始前的持续时间:

class Time(models.Model):
    # A participator of a race
    bib = models.ForeignKey(Bib, on_delete=models.CASCADE)
    # A timing point. Could be one at 0km, 5km and at the goal
    timing_point = models.ForeignKey(TimingPoint, on_delete=models.CASCADE)
    # The duration from start
    time = models.FloatField()

我想为每个围嘴计算两个计时点之间的持续时间。结果应该是每个围兜有一行的QuerySet。我认为如果可以在一个数据库查询中完成数据获取和持续时间计算,那将是最有效的。能做到这一点吗?

要弄清目标,这就是我现在要做的(删除了异常处理):

    times = []
    for bib in bibs:       
        from_time = Time.objects.get(timing_point=from_point, bib=bib)        
        to_time = Time.objects.get(timing_point=to_point, bib=bib)
        times.append({'bib': bib, 'time': to_time.time - from_time.time, })        

我想避免多次访问数据库,因此我希望结果是否为QuerySet,因为我希望能够执行其他操作,例如订购。

1 个答案:

答案 0 :(得分:4)

及更高版本中,我们可以将.annotate(..)Min之类的聚合一起使用,并在该聚合上使用filter=...条件,例如:

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

t2=Min('time__time', filter=Q(time__timing_point=to_point))
t1=Min('time__time', filter=Q(time__timing_point=from_point))

Bib.objects.annotate(dtime=t2-t1)

我们首先介绍两个变量:

    对于与相关 t2的对象,
  1. time将包含最小的Timetiming_pointto_point(可能只有一);和
  2. 对于与相关 t1的对象,
  3. time将包含最小的Time,其中timing_pointfrom_point

然后我们用dtimet2之间的区别进行注释t1

由于这仍然是QuerySet的一部分,因此我们甚至可以在Bib上订购dtime,等等。

Django会将其转换为如下查询:

SELECT bib.*,
       (MIN(CASE WHEN time.timing_point_id = 2 THEN time.time ELSE NULL END) -
        MIN(CASE WHEN time.timing_point_id = 1 THEN time.time ELSE NULL END)) AS dtime
FROM bib
LEFT OUTER JOIN time ON bib.id = time.bib_id GROUP BY bib.id

实际上,21的主键分别是{​​{1}}和to_point

如果您也过滤from_point模型,它可能会进一步提高查询效率:

Timing

这将导致查询如下:

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

t2=Min('time__time', filter=Q(time__timing_point=to_point))
t1=Min('time__time', filter=Q(time__timing_point=from_point))

Bib.objects.filter(
    time__timing_point__in=[from_point, to_point]
).annotate(dtime=t2-t1)