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

如何在Django中从多对多字段获取逗号分隔的关联建筑类型列表?

Django多对多关联数据逗号分隔展示方案

问题场景

在projects应用中,ProjectInfo与Buildingtypes是多对多关联,需要在列表中展示每个项目及其逗号分隔的关联建筑类型。

模型代码:

class Buildingtypes(models.Model):
    buildingtype = models.CharField(max_length=100)

    def __str__(self):
        return self.buildingtype

class ProjectInfo(models.Model):
    project_number = models.CharField(max_length=12, blank=True)
    project_name = models.CharField(max_length=120)
    buildingtypes = models.ManyToManyField(Buildingtypes, default=[1])

    def __str__(self):
        return self.project_name

此前尝试的模板无法正常显示关联数据,而直接用MySQL的GROUP_CONCAT能得到预期结果,现提供两种Django层面的解决方案:


方案一:模型内添加属性拼接字符串

在ProjectInfo模型中新增一个属性,直接返回逗号分隔的建筑类型字符串,方便模板调用。

修改模型

class ProjectInfo(models.Model):
    # 原有字段不变
    project_number = models.CharField(max_length=12, blank=True)
    project_name = models.CharField(max_length=120)
    buildingtypes = models.ManyToManyField(Buildingtypes, default=[1])

    def __str__(self):
        return self.project_name
    
    @property
    def buildingtypes_str(self):
        # 遍历关联的建筑类型,拼接成字符串
        return ', '.join([bt.buildingtype for bt in self.buildingtypes.all()])

优化视图(避免N+1查询)

为减少数据库查询次数,用prefetch_related预加载多对多关联数据:

from django.http import HttpResponse
from django.template import loader
from .models import ProjectInfo

def index(request):
    # 预加载buildingtypes关联数据
    myProjects = ProjectInfo.objects.all().prefetch_related('buildingtypes')
    template = loader.get_template('projects/index.html')
    context = {
        'projects': myProjects,
        }
    return HttpResponse(template.render(context, request))

修改模板

直接调用新增的属性即可:

{% if projects %}
    {% for x in projects %}
        {{ x.project_number }} | {{ x.project_name }} | {{ x.buildingtypes_str }}
    {% endfor %}
{% endif %}

方案二:ORM结合MySQL的GROUP_CONCAT(数据库层面拼接)

如果希望直接在数据库层面完成字符串拼接,可自定义GroupConcat函数配合annotate实现。

定义GroupConcat函数

在模型文件或视图文件中添加:

from django.db.models import Func, Value, CharField

class GroupConcat(Func):
    function = 'GROUP_CONCAT'
    template = "%(function)s(%(expressions)s SEPARATOR '%(separator)s')"
    arity = 1

    def __init__(self, expression, separator=', ', **extra):
        super().__init__(
            expression,
            separator=Value(separator),
            output_field=CharField(),
            **extra
        )

修改视图

用annotate为每个项目添加拼接后的字段:

from django.http import HttpResponse
from django.template import loader
from .models import ProjectInfo
from .models import GroupConcat  # 或根据定义位置导入

def index(request):
    myProjects = ProjectInfo.objects.annotate(
        buildingtypes_str=GroupConcat('buildingtypes__buildingtype')
    ).all()
    template = loader.get_template('projects/index.html')
    context = {
        'projects': myProjects,
        }
    return HttpResponse(template.render(context, request))

模板调用

和方案一相同,直接调用{{ x.buildingtypes_str }}即可。


此前尝试失败的原因

  1. 第一次模板使用{{ x.buildingtype }},但模型中多对多字段名为buildingtypes(复数),直接调用单数字段不存在,因此显示None。
  2. 第二次模板的循环未显示内容,大概率是未用prefetch_related预加载关联数据,导致延迟加载时未获取到数据;即便显示,也只是逐个输出,无法自动拼接为逗号分隔的字符串。

内容的提问来源于stack exchange,提问作者Kristian Røstøen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:27:05