咨询Django中与多次filter等价的ManyToManyField更优查询方式
Django多对多关联的精确交集查询
先理清楚各写法的差异
你要的是同时关联了ID为1-5所有Publication的Article,但另外两种写法都没命中需求:
- 用
Q(publications=1) & Q(publications=2)...完全不对,它想找单条关联记录同时属于两个不同的Publication,这根本不可能存在; publications__id__in=[1,2,3,4,5]是只要关联了其中任意一个Publication的Article都会被捞出来,范围比你要的大太多;- 多次链式
filter之所以能得到正确结果,是因为每次filter都会重新关联publications表,逐步筛选出同时满足所有条件的记录,但这种写法太啰嗦,而且数据库查询效率不高。
更简洁高效的实现方式
用annotate配合Count统计每个Article关联的指定Publication数量,再筛选数量等于目标ID总数的记录,一次查询搞定:
from django.db.models import Count target_pubs = [1, 2, 3, 4, 5] result = Article.objects.filter(publications__id__in=target_pubs)\ .annotate(matched_pubs=Count('publications', distinct=True))\ .filter(matched_pubs=len(target_pubs))
简单解释下逻辑
- 第一步先把所有关联了任意一个目标Publication的Article捞出来;
- 用
annotate统计每个Article关联的不重复的目标Publication数量(加distinct=True是防止同一Article重复关联某个Publication时计数出错); - 最后只保留计数等于目标ID总数的Article,确保它关联了所有指定的Publication。
补充:为啥链式filter能行?
每次链式调用filter(publications=X),Django都会生成一次新的JOIN操作,每次都在之前的结果集里再筛选一遍,最终得到同时关联所有指定Publication的记录。但这种方式会生成多次JOIN,性能不如上面的单查询写法。
内容的提问来源于stack exchange,提问作者EddyHD
相关产品推荐
相关产品推荐

