Django多对多关联对象聚合为数组的ORM查询方案
解决方案
针对SQLite环境下Django多对多关联的聚合需求,我们可以通过数据库层面的字段拼接+Python轻量拆分实现高效的结果聚合,避免返回重复记录。
步骤1:自定义SQLite GROUP_CONCAT函数封装
首先封装SQLite原生的GROUP_CONCAT函数,让Django ORM可以直接调用:
from django.db.models import Func, Value class GroupConcat(Func): function = 'GROUP_CONCAT' template = "%(function)s(%(expressions)s, '%(separator)s')" def __init__(self, expression, separator=',', **extra): super().__init__( expression, separator=Value(separator), **extra )
这个类会生成SQL语句GROUP_CONCAT(authors__name, ','),将同一书籍的所有作者名字用逗号拼接成字符串。
步骤2:修改查询方法,添加聚合逻辑
在你的__model_query方法中,针对Book模型添加聚合注解,同时用distinct()确保每个书籍只返回一条记录:
def __model_query(self, model_class, fields=None, enum_fields=None, order_by=None, search=None, field_search=None): queryset = model_class.objects.all() # 原有排序逻辑 if order_by: field_name, sort_order = order_by if sort_order == "desc": queryset = queryset.order_by(f"-{field_name}") elif sort_order == "asc": queryset = queryset.order_by(field_name) # 原有通用搜索逻辑 if search: search_filters = Q() for field in fields: search_filters |= Q(**{f"{field}__icontains": search}) queryset = queryset.filter(search_filters) # 原有字段专属搜索逻辑 if field_search: search_filters = Q() for field, value in field_search.items(): if enum_fields and field in enum_fields: search_filters |= Q(**{f"{field}__exact": value}) else: search_filters |= Q(**{f"{field}__icontains": value}) queryset = queryset.filter(search_filters) # 新增:针对Book模型聚合作者名字 if model_class == Book: queryset = queryset.annotate( authors_names=GroupConcat('authors__name') ).distinct() return queryset
步骤3:转换聚合结果为列表
获取查询集后,将拼接的字符串拆分为作者名字列表:
# 调用查询方法获取书籍数据 book_queryset = self.__model_query(Book, fields=['title']) # 转换为目标格式 result = [] for book in book_queryset: # 处理空值情况,避免split报错 author_names = book.authors_names.split(',') if book.authors_names else [] result.append({ "id": book.id, "title": book.title, "authors__name": author_names })
关键说明
- 性能优势:聚合操作在数据库层面完成,减少了数据库返回的记录行数,大幅降低数据传输和内存占用,比Python层面去重合并更高效。
- 分隔符选择:如果作者名字可能包含逗号,可在
GroupConcat初始化时指定其他分隔符(如separator=';'),避免拆分错误。 - 兼容性:完全基于SQLite原生函数和Django标准ORM,无需依赖PostgreSQL专属特性。
内容的提问来源于stack exchange,提问作者Imad Fen
相关产品推荐
相关产品推荐

