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

Django如何在模板for循环中过滤展示两个关联模型数据

Django 患者-就诊记录关联展示方案

问题核心

原有实现同时全量查询两张表数据传入模板,手动循环匹配时未利用ORM外键关联能力,要么出现患者总数×就诊记录总数的笛卡尔积错误,要么无法正确匹配单条就诊记录对应的患者信息。

分步实现

1. 模型层修正

原模型存在命名不规范、冗余方法、外键字段定义不合理的问题,修正后代码如下:

from django.db import models

class PatientProfile(models.Model):
    pat_Fname = models.CharField(max_length=100, verbose_name="患者名")
    pat_Lname = models.CharField(max_length=100, blank=True, verbose_name="患者姓")

    class Meta:
        verbose_name = "患者档案"
        verbose_name_plural = verbose_name

class Visits(models.Model):
    # 定义外键,指定related_name用于反向从患者查询关联就诊记录
    pat = models.ForeignKey(
        PatientProfile, 
        on_delete=models.CASCADE, 
        related_name="visits",
        verbose_name="所属患者"
    )
    visit_date = models.DateField(blank=False, null=False, verbose_name="就诊日期")
    visit_time = models.TimeField(blank=False, null=False, verbose_name="就诊时间")

    class Meta:
        verbose_name = "就诊记录"
        verbose_name_plural = verbose_name

说明:原模型中自定义的IntegerField方法无实际作用,Django默认会为模型生成自增id主键,无需额外定义;外键字段不需要手动加_ID后缀,Django会自动在数据库层生成带_id的关联字段。


2. 视图层写法

根据展示需求二选一即可,两种写法都通过ORM提供的关联查询优化接口避免N+1查询问题,不需要同时传入两张表的全量数据:

  • 需求1:按就诊记录维度展示,每条记录对应一行,展示所属患者信息
from django.contrib.auth.decorators import login_required
from .models import Visits

@login_required
def visitslist(request):
    # select_related通过JOIN一次性查出每条就诊记录关联的患者数据
    vis = Visits.objects.select_related('pat').all()
    return render(request, 'pmp/visitslist.html', {'vis': vis})
  • 需求2:按患者维度展示,每个患者下面列出自己的所有就诊记录,无记录则提示
from django.contrib.auth.decorators import login_required
from .models import PatientProfile

@login_required
def visitslist(request):
    # prefetch_related预取所有患者关联的就诊记录,在Python层完成匹配
    patpro = PatientProfile.objects.prefetch_related('visits').all()
    return render(request, 'pmp/visitslist.html', {'patpro': patpro})

3. 模板层写法

对应上面两种视图场景,直接通过ORM关联关系取数即可,不需要手动过滤匹配:

对应需求1(就诊记录维度)

直接通过外键字段pat访问关联的患者属性,不会出现笛卡尔积:

<table border="1" cellpadding="8" cellspacing="0">
  <thead>
    <tr>
      <th>就诊时间</th>
      <th>就诊日期</th>
      <th>患者ID</th>
      <th>患者姓名</th>
    </tr>
  </thead>
  <tbody>
    {% for visit in vis %}
    <tr>
      <td>{{ visit.visit_time }}</td>
      <td>{{ visit.visit_date }}</td>
      <td>{{ visit.pat.id }}</td>
      <td>{{ visit.pat.pat_Fname }} {{ visit.pat.pat_Lname }}</td>
    </tr>
    {% empty %}
    <tr>
      <td colspan="4" align="center">暂无就诊记录</td>
    </tr>
    {% endfor %}
  </tbody>
</table>

对应需求2(患者维度)

两层循环仅遍历当前患者自己的关联就诊记录,不会出现全量匹配的笛卡尔积问题:

{% for patient in patpro %}
<div style="margin: 16px 0;">
  <h4>患者信息:{{ patient.pat_Fname }} {{ patient.pat_Lname }}(ID:{{ patient.id }})</h4>
  <table border="1" cellpadding="8" cellspacing="0">
    <thead>
      <tr>
        <th>就诊日期</th>
        <th>就诊时间</th>
      </tr>
    </thead>
    <tbody>
      {% for visit in patient.visits.all %}
      <tr>
        <td>{{ visit.visit_date }}</td>
        <td>{{ visit.visit_time }}</td>
      </tr>
      {% empty %}
      <tr>
        <td colspan="2" align="center">该患者暂无就诊记录</td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
</div>
{% endfor %}

注意事项

  • 多对一/一对一关联查询(如从就诊记录查所属患者)优先用select_related,通过SQL JOIN一次取数,性能更高
  • 一对多/多对多关联查询(如从患者查所有就诊记录)优先用prefetch_related,分两次查询后在内存中匹配,避免大表JOIN性能问题
  • 不要在模板中手动做跨表数据过滤匹配,应该在视图层通过ORM完成数据关联后再传入模板,避免出现笛卡尔积或额外的性能开销

内容的提问来源于stack exchange,提问作者Saravanan Ragavan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:54:17