Django Taggit如何在单条QuerySet中聚合获取所有关联标签名
django-taggit 批量获取QuerySet关联标签实现方案
你要的QuerySet层面批量拉取关联标签、避免N+1查询的效果,可通过以下方案实现,完全匹配你给出的调用预期:
基础预加载方案(全数据库兼容)
如果只是需要遍历QuerySet时访问标签不触发额外查询,直接使用prefetch_related预拉取关联标签即可,全程仅触发2条SQL(1条查询目标Book集合,1条查询所有关联的标签数据):
# 预加载标签数据,仅查询标签必要的name字段减少数据传输 books = Book.objects.filter(year=2020).prefetch_related('tags') # 遍历访问标签不会触发额外SQL查询 for book in books: # 直接获取标签名列表 tag_names = list(book.tags.names())
直接返回标签列表字段的values实现
PostgreSQL 环境(单条查询实现)
如果你使用PostgreSQL作为数据库,可通过内置的数组聚合函数ArrayAgg直接在查询阶段注解出标签名列表字段,全程仅1条SQL,完全匹配你给出的预期代码写法:
from django.contrib.postgres.aggregates import ArrayAgg result = Book.objects.filter(year=2020).annotate( # 加distinct避免多对多关联导致的重复值 tag_names_list=ArrayAgg('tags__name', distinct=True) ).values('bookname', 'published_at', 'tag_names_list')[:]
返回的结果中,每一条字典数据都会直接包含tag_names_list字段,值为对应图书所有标签名组成的列表。
MySQL/SQLite 等非PostgreSQL环境
这类数据库不支持原生数组聚合,可在预加载标签的基础上做一层简单的Python层组装,全程仅2条SQL,性能损耗极低:
# 先查出带预加载标签的图书集合 book_list = list(Book.objects.filter(year=2020).prefetch_related('tags')) # 组装成你需要的字典结构 result = [ { 'bookname': book.bookname, 'published_at': book.published_at, 'tag_names_list': list(book.tags.names()) } for book in book_list ]
注意:未添加
prefetch_related时,不要在循环中直接访问单个对象的标签属性,会触发N+1查询问题,数据量较大时会产生严重的性能损耗。
内容的提问来源于stack exchange,提问作者AlexA
相关产品推荐
相关产品推荐

