简体   繁体   English

Django:使用前缀和注释模型

[英]Django: Annotate model with a prefix sum

I have a model TimeTrial that aggregates an ordered list of Leg s. 我有一个TimeTrial模型,该模型汇总了Leg的有序列表。

class TimeTrial(models.Model):
    objects = TimeTrialManager()

class Leg(models.Model):
    timetrial = models.ForeignKey(TimeTrial)
    duration = models.FloatField()
    order = models.PositiveIntegerField(default=0)

    @property
    def duration_prefix_sum(self):
        qs = Leg.objects.filter(timetrial=self.timetrial,
                                order__lte=self.order)
        return qs.aggregate(Sum('duration'))

class TimeTrialManager(models.Manager):
    def get_queryset(self):
        qs = super(TimeTrialManager, self).get_queryset()
        qs = qs.annotate(leg_count=Count('leg'))
        qs = qs.annotate(duration=Sum('leg__duration'))
        return qs

(Stripped-down version of the actual models .) 实际模型的精简版。)

The TimeTrialManager annotates each TimeTrial with the number of Leg s and the sum of Leg.duration . 所述TimeTrialManager注释每TimeTrial与数量Leg S和的总和Leg.duration I would like to compute the duration_prefix_sum using Django's annotate / aggregate in a similar way, so I don't incur a database hit when I display every leg's duration_prefix_sum in timetrial.leg_set.all() . 我想以类似的方式使用Django的annotate / aggregate来计算duration_prefix_sum ,所以当我在timetrial.leg_set.all()显示每条腿的duration_prefix_sum时,不会引起数据库命中。

I know I can write Python code to compute it: 我知道我可以编写Python代码来计算它:

timetrial.leg_set_annotated = []
s = 0
for l in timetrial.leg_set.all():
    s += l.duration
    l.duration_prefix_sum_computed = s
    timetrial.leg_set_annotated.append(l)

But I would rather use annotate/aggregate/Case/When of Django. 但是我宁愿使用Django的注释/聚合/案例/ When。

I've found a solution, but I'm not sure it's entirely robust. 我已经找到了解决方案,但是我不确定它是否完全可靠。 I've implemented it in my project . 我已经在我的项目中实现了它

We add the filter timetrial__leg__order__lte=F('order') to add a join containing all Legs with order less or equal to the current. 我们添加过滤器timetrial__leg__order__lte=F('order')来添加包含所有阶数小于或等于当前的timetrial__leg__order__lte=F('order') Then, we add the annotation duration_prefix_sum as Sum('timetrial__leg__duration') , which computes the prefix sum. 然后,我们将注释duration_prefix_sum添加为Sum('timetrial__leg__duration') ,从而计算前缀和。

from django.db.models import Sum, F
qs = Leg.objects.filter(timetrial__leg__order__lte=F('order'))
qs = qs.annotate(duration_prefix_sum=Sum('timetrial__leg__duration'))

I think this will fail if the code that uses this queryset makes another annotation/filter on 'timetrial__leg' , so additions to make this more robust are welcome. 我认为,如果使用此查询集的代码在'timetrial__leg'添加另一个注释/过滤器,则此操作将失败,因此欢迎添加使其更健壮的附加功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM