- 示例models
- 解决方法
有时候,我们想要获取一个对象关联关系的数量,但是我们不要所有的关联对象,我们只想要符合规则的那些关联对象的数量。
示例models
# models.py
from django.db import models
class Person(models.Model):
name = models.CharField('名称', max_length=32)
def __str__(self):
return self.name
class Task(models.Model):
name = models.CharField('任务名称', max_length=64)
is_done = models.BooleanField('是否完成', default=False)
owner = models.ForeignKey(Person, verbose_name='任务执行人')
def __str__(self):
return "{}({})".format(self.name, 'x' if self.is_done else 'o')
上面的代码中,我们使用了两个model,Person
和Task
。它们是一对多关系,一个用户可能有多个任务。
那么,我们怎么来获取一个用户已完成任务的数量呢?
解决方法
一般来说,都是使用下面这个办法:
>>> person = Person.objects.first()
>>> person.task_set.all().count()
15
>>> person.task_set.filter(is_done=True).count()
10
这个办法,确实可以解决问题。但是如果你有很多Person,都需要获取已完成任务的数量,使用这个方法会依次对每个person的tasks集合使用单独的SQL,开销极大。
我们知道,Django ORM有一个aggregate函数,叫做Count
。一般情况下,可以用它来获取一个对象关联对象的数量,并生成一个额外的computed字段。而在Django2.0中1,Count
新加入了一个参数filter
,使用它,可以在计算count数量之前先对集合进行过滤:
>>> from django.db.models import Count, Q
>>> person = Person.objects.annotate(
... Count('task_set', filter=Q(task_set__is_done=True))
... ).first()
>>> person.task_set__count
10
而在Django2.0之前,我们应该怎么办呢?可以用SQL的Case/When
来解决:
>>> from django.db.models import Count, When, Case
>>> person = Person.objects.annotate(Count(Case(When(task_set__is_done=True, then=0))).first()
>>> person.task_set__count
10
-
Django API Reference: Count ↩