Django ORM:如何在查询集中拼接/分组ManyToMany字段值
Django ORM实现GROUP_CONCAT效果的方案
不用非得写原生SQL,Django ORM通过annotate结合聚合函数就能实现类似MySQL GROUP_CONCAT的效果,下面是具体解决思路:
1. 适配PostgreSQL:用内置StringAgg(Django 3.2+支持)
如果你的项目基于PostgreSQL,直接用Django内置的聚合类即可:
from django.db.models import StringAgg, Max from .models import Handover result = Handover.objects.values('machine_name') \ .annotate( latest_handover_id=Max('id'), # 假设id自增,最新id对应最新记录 ct_limitations_concat=StringAgg('ct_limitations__name', delimiter=', ') ) \ .order_by('machine_name')
注意把ct_limitations__name替换成你ManyToMany关联模型中实际用来展示的字段名。
2. 适配MySQL:自定义聚合函数
MySQL没有对应的内置聚合类,你可以自己定义一个适配GROUP_CONCAT的聚合类:
from django.db.models import Aggregate, CharField class GroupConcat(Aggregate): function = 'GROUP_CONCAT' template = '%(function)s(%(expressions)s SEPARATOR "%(separator)s")' def __init__(self, expression, separator=', ', **kwargs): super().__init__( expression, separator=separator, output_field=CharField(), **kwargs )
然后在查询中使用这个自定义聚合:
from django.db.models import Max from .models import Handover result = Handover.objects.values('machine_name') \ .annotate( latest_handover_id=Max('id'), ct_limitations_concat=GroupConcat('ct_limitations__name', separator=', ') ) \ .order_by('machine_name')
3. 精准获取最新记录的关联字段
如果需要确保拿到的是最新handover记录的关联字段(而非分组后随机取的),可以结合子查询实现:
from django.db.models import Subquery, OuterRef, Max from .models import Handover # 先获取每个machine_name对应的最新handover_id latest_handover_ids = Handover.objects.filter( machine_name=OuterRef('machine_name') ).values('machine_name').annotate(latest_id=Max('id')).values('latest_id') # 基于最新id查询,同时聚合ct_limitations result = Handover.objects.filter(id__in=Subquery(latest_handover_ids)) \ .values('machine_name', 'id') \ .annotate(ct_limitations_concat=GroupConcat('ct_limitations__name'))
这样就能得到你预期的5条唯一机器记录,其中包含一条拼接了两个ct限制值的结果。
内容的提问来源于stack exchange,提问作者Nate Saunders
相关产品推荐
相关产品推荐

