rud*_*ryk 109 python django django-models django-aggregation
考虑简单的Django模型Event
和Participant
:
class Event(models.Model):
title = models.CharField(max_length=100)
class Participant(models.Model):
event = models.ForeignKey(Event, db_index=True)
is_paid = models.BooleanField(default=False, db_index=True)
Run Code Online (Sandbox Code Playgroud)
使用参与者总数来注释事件查询很容易:
events = Event.objects.all().annotate(participants=models.Count('participant'))
Run Code Online (Sandbox Code Playgroud)
如何用过滤的参与者数量进行注释is_paid=True
?
我需要查询所有事件而不管参与者的数量,例如,我不需要按注释结果进行过滤.如果有0
参与者,那没关系,我只需要0
注释值.
文档中的示例在此处不起作用,因为它从查询中排除对象而不是使用它进行注释0
.
更新.Django 1.8具有新的条件表达式功能,所以现在我们可以这样做:
events = Event.objects.all().annotate(paid_participants=models.Sum(
models.Case(
models.When(participant__is_paid=True, then=1),
default=0,
output_field=models.IntegerField()
)))
Run Code Online (Sandbox Code Playgroud)
rud*_*ryk 92
刚刚发现Django 1.8具有新的条件表达式功能,所以现在我们可以这样做:
events = Event.objects.all().annotate(paid_participants=models.Sum(
models.Case(
models.When(participant__is_paid=True, then=1),
default=0, output_field=models.IntegerField()
)))
Run Code Online (Sandbox Code Playgroud)
Oli*_*Oli 73
Django 2.0中的条件聚合允许您进一步减少过去的faff数量.这也将使用Postgres的filter
逻辑,这比一个总和案例(我已经看到数字,如20-30%左右).
无论如何,在你的情况下,我们正在寻找一些简单的东西:
from django.db.models import Q, Count
events = Event.objects.annotate(
paid_participants=Count('participants', filter=Q(participants__is_paid=True))
)
Run Code Online (Sandbox Code Playgroud)
文档中有一个关于过滤注释的单独部分.它与条件聚合相同,但更像我上面的例子.无论哪种方式,这比我以前做的粗糙的子查询更健康.
Tod*_*dor 39
UPDATE
我提到的子查询方法现在通过子查询表达式在Django 1.11中得到支持.
Event.objects.annotate(
num_paid_participants=Subquery(
Participant.objects.filter(
is_paid=True,
event=OuterRef('pk')
).values('event')
.annotate(cnt=Count('pk'))
.values('cnt'),
output_field=models.IntegerField()
)
)
Run Code Online (Sandbox Code Playgroud)
我更喜欢这种聚合(sum + case),因为它应该更快更容易优化(使用适当的索引).
对于旧版本,可以使用相同的版本 .extra
Event.objects.extra(select={'num_paid_participants': "\
SELECT COUNT(*) \
FROM `myapp_participant` \
WHERE `myapp_participant`.`is_paid` = 1 AND \
`myapp_participant`.`event_id` = `myapp_event`.`id`"
})
Run Code Online (Sandbox Code Playgroud)
我建议改用.values
您的Participant
queryset 方法。
简而言之,您想要做的是:
Participant.objects\
.filter(is_paid=True)\
.values('event')\
.distinct()\
.annotate(models.Count('id'))
Run Code Online (Sandbox Code Playgroud)
完整的示例如下:
创建2 Event
秒:
event1 = Event.objects.create(title='event1')
event2 = Event.objects.create(title='event2')
Run Code Online (Sandbox Code Playgroud)将Participant
s 添加到他们:
part1l = [Participant.objects.create(event=event1, is_paid=((_%2) == 0))\
for _ in range(10)]
part2l = [Participant.objects.create(event=event2, is_paid=((_%2) == 0))\
for _ in range(50)]
Run Code Online (Sandbox Code Playgroud)将所有Participant
s按其event
字段分组:
Participant.objects.values('event')
> <QuerySet [{'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, '...(remaining elements truncated)...']>
Run Code Online (Sandbox Code Playgroud)
这里需要不同的:
Participant.objects.values('event').distinct()
> <QuerySet [{'event': 1}, {'event': 2}]>
Run Code Online (Sandbox Code Playgroud)
什么.values
和.distinct
正在做的事情是,他们正在创造的两个水桶Participant
用元的分组小号event
。请注意,这些存储桶包含Participant
。
然后,您可以注释这些存储桶,因为它们包含原始集Participant
。在这里,我们要计算的数量Participant
,只需计算id
这些存储区中元素的s即可(因为它们是Participant
):
Participant.objects\
.values('event')\
.distinct()\
.annotate(models.Count('id'))
> <QuerySet [{'event': 1, 'id__count': 10}, {'event': 2, 'id__count': 50}]>
Run Code Online (Sandbox Code Playgroud)最后,您只Participant
需要一个is_paid
being True
,您可以只在前一个表达式前面添加一个过滤器,这将产生上面显示的表达式:
Participant.objects\
.filter(is_paid=True)\
.values('event')\
.distinct()\
.annotate(models.Count('id'))
> <QuerySet [{'event': 1, 'id__count': 5}, {'event': 2, 'id__count': 25}]>
Run Code Online (Sandbox Code Playgroud)唯一的缺点是Event
您只能id
从上面的方法中获取,因此您必须检索之后的内容。
归档时间: |
|
查看次数: |
44110 次 |
最近记录: |