Nel*_*son 3 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()
Run Code Online (Sandbox Code Playgroud)
我想计算每个围兜的两个计时点之间的持续时间。结果应该是一个查询集,每个号码布一行。我认为如果数据获取和持续时间计算可以在一个数据库查询中完成,那将是最有效的。这可以做到吗?如何做到?
为了澄清目标,我现在这样做(删除了异常处理):
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, })
Run Code Online (Sandbox Code Playgroud)
我想避免多次访问数据库,并且我更希望结果是一个 QuerySet,因为我希望能够执行其他操作,例如排序。
在django-2.0及更高版本中,我们可以使用.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)Run Code Online (Sandbox Code Playgroud)
我们首先引入两个变量:
t2将包含与相关的最小time对象(可能只有一个);和 Timetiming_pointto_pointt1将包含具有的相关对象time的最小者。 Timetiming_pointfrom_point然后我们对和dtime之间的差异进行注释。t2t1
由于这仍然是 的一部分QuerySet,我们甚至可以Bib在 等上订购 s 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
Run Code Online (Sandbox Code Playgroud)
实际上,2和分别是和的主键。1to_pointfrom_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)Run Code Online (Sandbox Code Playgroud)
这将导致一个如下所示的查询:
SELECT bib.*,
(MIN(CASE WHEN time.timing_point_id = 2 THEN time.time ELSE NULL END) -
MIN(CASE WHEN time.timing_point_id = 2 THEN time.time ELSE NULL END)) AS dtime
FROM bib LEFT OUTER JOIN time ON bib.id = time.bib_id
WHERE time.timing_point_id IN (1, 2)
GROUP BY bib.idRun Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1369 次 |
| 最近记录: |