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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:51:18