You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    })

关键说明

  1. 性能优势:聚合操作在数据库层面完成,减少了数据库返回的记录行数,大幅降低数据传输和内存占用,比Python层面去重合并更高效。
  2. 分隔符选择:如果作者名字可能包含逗号,可在GroupConcat初始化时指定其他分隔符(如separator=';'),避免拆分错误。
  3. 兼容性:完全基于SQLite原生函数和Django标准ORM,无需依赖PostgreSQL专属特性。

内容的提问来源于stack exchange,提问作者Imad Fen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:14:57