如何在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 }}即可。
此前尝试失败的原因
- 第一次模板使用
{{ x.buildingtype }},但模型中多对多字段名为buildingtypes(复数),直接调用单数字段不存在,因此显示None。 - 第二次模板的循环未显示内容,大概率是未用
prefetch_related预加载关联数据,导致延迟加载时未获取到数据;即便显示,也只是逐个输出,无法自动拼接为逗号分隔的字符串。
内容的提问来源于stack exchange,提问作者Kristian Røstøen
相关产品推荐
相关产品推荐

