如何在 Django ORM 中按条件分组和聚合
How to group by and aggregate conditional in Django ORM
我有一个 Django 查询来获取每个员工每天的平均工作时间(每天可能有多个工作日志)。我想在工作日和周末工作中将它们分开。
这是工作日志模型:
class Worklog(models.Model):
worker = models.ForeignKey(Person, on_delete=models.CASCADE)
day = models.DateField(null=False, blank=False)
effort = models.FloatField()
comment = models.TextField(null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
我尝试了以下方法:
qs = Worklog.objects.filter(
day__range=[start,end]
).values(
'worker__fullname'
).annotate(
weekday=Case(
When(Q(day__week_day=1) | Q(day__week_day=7), then=1),
default=0,
output_field=IntegerField(),
)
).values(
'worker__fullname'
).annotate(
weekdayAvg=Case(
When(Q(weekday=0), then=Cast(
Sum('effort')/Count('day', distinct=True)/60/60, FloatField()
)),
default=0,
output_field=FloatField(),
),
weekendAvg=Case(
When(Q(weekday=1), then=Cast(
Sum('effort')/Count('day', distinct=True)/60/60, FloatField()
)),
default=0,
output_field=FloatField(),
)
).order_by('worker__fullname')
这给了我结果
weekdayAvg weekendAvg worker__fullname
0 9.125000 0.00 Klaus
1 0.000000 11.00 Klaus
2 6.977273 0.00 Peter
3 7.827586 0.00 Carl
4 0.000000 13.00 Carl
5 8.169643 0.00 Chris
6 0.000000 2.25 Chris
然而,预期的结果更像是:
weekdayAvg weekendAvg worker__fullname
0 9.125000 11.00 Klaus
1 6.977273 0.00 Peter
2 7.827586 13.00 Carl
3 8.169643 2.25 Chris
有什么实现方法吗?
我也很高兴我的查询得到了一些简化。
谢谢!
我自己搞定了。我以前不知道的功能是在聚合函数中使用filter=
参数(Sum
、Count
等)
qs = Worklog.objects.filter(
day__range=[start,end]
).values(
'worker__fullname'
).annotate(
weekdayAvg=Cast(Coalesce(Sum('effort', filter=(~Q(day__week_day=1) & ~Q(day__week_day=7)))/Count('day', distinct=True, filter=(~Q(day__week_day=1) & ~Q(day__week_day=7)))/60/60, 0), FloatField()),
weekdayCnt=Cast(Coalesce(Count('day', distinct=True, filter=(~Q(day__week_day=1) & ~Q(day__week_day=7))), 0), FloatField()),
weekendAvg=Cast(Coalesce(Sum('effort', filter=(Q(day__week_day=1) | Q(day__week_day=7)))/Count('day', distinct=True, filter=(Q(day__week_day=1) | Q(day__week_day=7)))/60/60, 0), FloatField()),
weekendCnt=Cast(Coalesce(Count('day', distinct=True, filter=(Q(day__week_day=1) | Q(day__week_day=7))), 0), FloatField()),
).order_by('weekdayAvg', 'worker__fullname')
我还添加了 Coalesce(..., 0)
,否则总和将 return None
(根据 post)
我有一个 Django 查询来获取每个员工每天的平均工作时间(每天可能有多个工作日志)。我想在工作日和周末工作中将它们分开。
这是工作日志模型:
class Worklog(models.Model):
worker = models.ForeignKey(Person, on_delete=models.CASCADE)
day = models.DateField(null=False, blank=False)
effort = models.FloatField()
comment = models.TextField(null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
我尝试了以下方法:
qs = Worklog.objects.filter(
day__range=[start,end]
).values(
'worker__fullname'
).annotate(
weekday=Case(
When(Q(day__week_day=1) | Q(day__week_day=7), then=1),
default=0,
output_field=IntegerField(),
)
).values(
'worker__fullname'
).annotate(
weekdayAvg=Case(
When(Q(weekday=0), then=Cast(
Sum('effort')/Count('day', distinct=True)/60/60, FloatField()
)),
default=0,
output_field=FloatField(),
),
weekendAvg=Case(
When(Q(weekday=1), then=Cast(
Sum('effort')/Count('day', distinct=True)/60/60, FloatField()
)),
default=0,
output_field=FloatField(),
)
).order_by('worker__fullname')
这给了我结果
weekdayAvg weekendAvg worker__fullname
0 9.125000 0.00 Klaus
1 0.000000 11.00 Klaus
2 6.977273 0.00 Peter
3 7.827586 0.00 Carl
4 0.000000 13.00 Carl
5 8.169643 0.00 Chris
6 0.000000 2.25 Chris
然而,预期的结果更像是:
weekdayAvg weekendAvg worker__fullname
0 9.125000 11.00 Klaus
1 6.977273 0.00 Peter
2 7.827586 13.00 Carl
3 8.169643 2.25 Chris
有什么实现方法吗?
我也很高兴我的查询得到了一些简化。 谢谢!
我自己搞定了。我以前不知道的功能是在聚合函数中使用filter=
参数(Sum
、Count
等)
qs = Worklog.objects.filter(
day__range=[start,end]
).values(
'worker__fullname'
).annotate(
weekdayAvg=Cast(Coalesce(Sum('effort', filter=(~Q(day__week_day=1) & ~Q(day__week_day=7)))/Count('day', distinct=True, filter=(~Q(day__week_day=1) & ~Q(day__week_day=7)))/60/60, 0), FloatField()),
weekdayCnt=Cast(Coalesce(Count('day', distinct=True, filter=(~Q(day__week_day=1) & ~Q(day__week_day=7))), 0), FloatField()),
weekendAvg=Cast(Coalesce(Sum('effort', filter=(Q(day__week_day=1) | Q(day__week_day=7)))/Count('day', distinct=True, filter=(Q(day__week_day=1) | Q(day__week_day=7)))/60/60, 0), FloatField()),
weekendCnt=Cast(Coalesce(Count('day', distinct=True, filter=(Q(day__week_day=1) | Q(day__week_day=7))), 0), FloatField()),
).order_by('weekdayAvg', 'worker__fullname')
我还添加了 Coalesce(..., 0)
,否则总和将 return None
(根据