使用子查询聚合的改进api
django-sql-utils的Python项目详细描述
django sql实用程序
这个包提供了使用django querysets的实用程序,以便 您可以使用自己喜欢的api生成所需的sql。
子查询聚合
django中的计数聚合:
Parent.objects.annotate(child_count=Count('child'))
生成SQL,如下所示:
SELECT parent.*, Count(child.id) as child_count FROM parent JOIN child on child.parent_id = parent.id GROUP BY parent.id
在许多情况下,这不如在子查询中执行计数。 而不是加入:
SELECT parent.*, (SELECT Count(id) FROM child WHERE parent_id = parent.id) as child_count FROM parent
django允许我们使用子查询和outerref类生成此sql:
subquery = Subquery(Child.objects.filter(parent_id=OuterRef('id')).order_by() .values('parent').annotate(count=Count('pk')) .values('count'), output_field=IntegerField()) Parent.objects.annotate(child_count=Coalesce(subquery, 0))
天哪!想清楚上面所说的每件事都在做些什么并不容易 代码,对维护不是特别好。SubQueryAggregates允许 你要忘记所有的复杂性,并生成这样的子查询计数:
Parent.objects.annotate(child_count=SubqueryCount('child'))
呸!更容易阅读和理解。它与原来的count是相同的api 只是指定子查询版本。
存在的更简单的API < EH3>
如果您有一个父/子关系(child有一个到父的foreignkey),则可以注释queryset 具有布尔值的父对象,该布尔值指示父对象是否有子对象:
from django.db.models import Exists parents = Parent.objects.annotate( has_children=Exists(Child.objects.filter(parent=OuterRef('pk')) )
这比需要的要多一些,因此我们提供了一个更简单的API:
from sql_util.utils import Exists parents = Parent.objects.annotate( has_children=Exists('child') )
子QuerySet可以用关键字参数过滤。例如:
parents = Parent.objects.annotate( has_child_named_John = Exists('child', filter=Q(name='John')) )<> >
安装和使用
从pypi安装:
pip install django-sql-utils
然后您可以:
from sql_util.utils import SubqueryCount
如上图所示。
除了subquerycount之外,此软件包还提供
- 子查询min
- 子查询最大值
- 子查询
- 子查询avg
如果要使用其他聚合,可以使用 泛型类。例如,如果要使用postgres'arrayagg 为每个父节点获取子节点的数组。name:
from django.contrib.postgres.aggregates import ArrayAgg aggregate = SubqueryAggregate('child__name', aggregate=ArrayAgg) Parent.objects.annotate(child_names=aggregate)
或子类subqueryaggregate:
from django.contrib.postgres.aggregates import ArrayAgg class SubqueryArrayAgg(SubqueryAggregate) aggregate = ArrayAgg unordered = True Parent.objects.annotate(avg_child_age=SubqueryArrayAgg('child__age'))