Django多表关联原生SQL转ORM查询及DTL模板渲染方法
Django多表关联ORM查询与模板渲染实现方案
1. 修复现有模型定义
你提供的models.py中Broadcast_Contact表的contact外键定义存在代码截断,先补全后才能正常执行迁移和查询:
class BroadcastList(models.Model): brl_name = models.CharField(max_length=100, null=False) class Contact(models.Model): f_name = models.CharField(max_length=100, null=False) class Broadcast_Contact(models.Model): broadcast = models.ForeignKey(BroadcastList,on_delete=models.CASCADE, null=True) # 补全原截断的外键定义 contact = models.ForeignKey(Contact,on_delete=models.CASCADE, null=True)
修改完成后执行python manage.py makemigrations和python manage.py migrate同步表结构。
2. 原生SQL对应的ORM查询实现
你写的原生SQL核心是3表JOIN后筛选broadcastID=2的关联数据,Django ORM会自动处理表关联逻辑,不需要手写JOIN,以下两种写法都能实现需求:
- 从广播维度查询(推荐,方便后续扩展按广播分组展示的需求)
利用外键反向关联+prefetch_related预加载避免N+1查询问题,性能最优:# 拿到ID=2的广播对象,同时预加载所有关联的联系人数据 target_broadcast = BroadcastList.objects.prefetch_related( 'broadcast_contact_set__contact' ).get(id=2) # 提取关联的所有联系人列表,自动去重避免中间表重复数据 contact_list = { rel.contact for rel in target_broadcast.broadcast_contact_set.all() } - 从联系人维度查询(和原生SQL执行逻辑完全等价)
直接通过中间表的关联条件筛选联系人:# 自动JOIN中间表+广播表,筛选关联到ID=2广播的所有联系人 contact_list = Contact.objects.filter( broadcast_contact__broadcast_id=2 ).distinct() target_broadcast = BroadcastList.objects.get(id=2)
注意:如果中间表可能存在同一广播+同一联系人的重复绑定记录,务必加上.distinct()去重,避免前端列表出现重复项。
3. 视图层传参逻辑
在对应app的views.py中编写视图函数,将查询到的QuerySet传递给模板:
from django.shortcuts import render from .models import BroadcastList def index(request): # 执行ORM查询 target_broadcast = BroadcastList.objects.prefetch_related( 'broadcast_contact_set__contact' ).get(id=2) contact_list = [rel.contact for rel in target_broadcast.broadcast_contact_set.all()] # 构造上下文传入模板 context = { 'broadcast': target_broadcast, 'contact_list': contact_list } return render(request, 'index.html', context)
写完视图后记得在urls.py中配置对应路由指向这个index视图。
4. DTL模板渲染代码
在模板目录下创建index.html,通过Django模板语法遍历渲染数据即可,和你期望的输出效果一致:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>广播关联列表</title> </head> <body> <div class="broadcast-card"> <h4>广播ID:{{ broadcast.id }}</h4> <p>广播名称:{{ broadcast.brl_name }}</p> <p>关联联系人列表:</p> <ul> {% for contact in contact_list %} <li>{{ contact.f_name }}</li> {% empty %} <li>当前广播未绑定任何联系人</li> {% endfor %} </ul> </div> </body> </html>
渲染后页面会输出ID=2的广播信息,以及关联的Priti、Tina、Rahul三个联系人。
可选优化:使用内置多对多字段简化代码
如果你的中间表不需要存储额外字段(比如绑定时间、操作人等),可以直接用Django内置的ManyToManyField替代手动创建的中间表,代码会更简洁:
class BroadcastList(models.Model): brl_name = models.CharField(max_length=100, null=False) # 声明多对多关联,Django自动创建中间关联表 contacts = models.ManyToManyField(Contact) class Contact(models.Model): f_name = models.CharField(max_length=100, null=False)
优化后查询逻辑可以简化为:
target_broadcast = BroadcastList.objects.prefetch_related('contacts').get(id=2) contact_list = target_broadcast.contacts.all()
模板中可以直接遍历broadcast.contacts.all,不需要再操作中间表的反向关联。
内容的提问来源于stack exchange,提问作者user19350831
相关产品推荐
相关产品推荐

