如何过滤Django中的计数注释对象?
考虑简单的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)
注释参与者总数的事件查询很容易:
events = Event.objects.all().annotate(participants=models.Count('participant'))
如何使用is_paid=True
过滤参与者的is_paid=True
?
我需要查询所有事件,无论参与者的数量,例如我不需要通过注释的结果过滤。 如果有0
参与者,没关系,我只需要注释值为0
。
文档中的示例在这里不起作用,因为它将查询中的对象排除在外,而不用0
注释它们。
更新。 Django 1.8有新的条件expression式function ,所以现在我们可以这样做:
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() )))
刚刚发现Django 1.8有新的条件expression式function ,所以现在我们可以这样做:
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() )))
UPDATE
我提到的子查询方法现在通过子查询expression式支持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() ) )
我更喜欢这个聚合(总和+情况) ,因为它应该更快,更容易被优化(适当的索引) 。
对于旧版本,使用.extra
可以实现相同的.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`" })