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

如何在Django模板中基于外键关联访问关联对象?

Django模板中外键关联数据的层级遍历实现方法

我有一组通过外键关联的Django模型,精简后的models.py代码如下:

class Saga(models.Model):
    name = models.CharField(max_length=200)
    description = models.CharField(max_length=1000)
    startChapter = models.PositiveIntegerField()
    endChapter = models.PositiveIntegerField()

class Arc(models.Model):
    name = models.CharField(max_length=200)
    description = models.CharField(max_length=1000)
    saga = models.ForeignKey(Saga,on_delete=models.SET_DEFAULT,default=Saga.get_default_pk)
    startChapter = models.PositiveIntegerField()
    endChapter = models.PositiveIntegerField()

class Clip(models.Model):
    description = models.CharField(max_length=200)
    arc = models.ForeignKey(Arc,on_delete=models.SET_DEFAULT,default=Arc.get_default_pk)
    chapter = models.PositiveIntegerField()
    viewers = models.ManyToManyField(User, through='ClipViewer')
    author = models.ForeignKey(User,on_delete=models.SET_NULL,null=True, related_name='author_of')

这些模型的关联关系是:每个Saga对应多个Arc,每个Arc对应多个Clip。我希望在模板中按层级遍历展示,结构示例如下:

Saga 1 has arcs 1,2,3
   Arc 1 has clips 1 & 2
   Arc 2 has clip 3
   Arc 3 has clips 4 & 5
Saga 2 has arc 4,5....

但Django模板功能有限,官方提示:

Django有意限制模板语言中的逻辑处理能力,因此无法在模板中调用带参数的方法。数据应在视图中计算完成后,再传递给模板展示。

以下是可行的解决方案:

方法一:利用自动生成的反向关联属性直接遍历

Django会为外键自动生成反向关联属性,默认名称为模型名小写_set,你可以直接在模板循环中使用这个属性获取关联数据:

模板代码示例:

{% for saga in sagas %}
  <p>{{ saga.name }} has arcs {% for arc in saga.arc_set.all %}{{ arc.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</p>
  {% for arc in saga.arc_set.all %}
    <p>&nbsp;&nbsp;&nbsp;&nbsp;{{ arc.name }} has clips {% for clip in arc.clip_set.all %}{{ clip.description }}{% if not forloop.last %} &amp; {% endif %}{% endfor %}</p>
  {% endfor %}
{% endfor %}

注意:这种方式在数据量大时会触发N+1查询问题,每次遍历都会执行新的数据库查询,性能较差。

方法二:视图预加载关联数据(推荐)

为优化性能,在视图中使用prefetch_related预加载所有层级的关联数据,一次性完成数据库查询,再传递给模板:

视图代码示例:

from django.shortcuts import render
from django.db.models import Prefetch
from .models import Saga, Arc

def saga_list(request):
    # 预加载Saga关联的所有Arc,以及每个Arc关联的Clip
    sagas = Saga.objects.prefetch_related(
        Prefetch('arc_set', queryset=Arc.objects.prefetch_related('clip_set'))
    ).all()
    return render(request, 'saga_list.html', {'sagas': sagas})

模板代码和方法一完全相同,但此时所有数据已在视图中一次性查询完成,性能显著提升。

可选:自定义反向关联名称

如果觉得默认的arc_set、clip_set不够直观,可以在定义外键时通过related_name指定自定义名称:

修改模型代码:

class Arc(models.Model):
    # ... 其他字段
    saga = models.ForeignKey(
        Saga, 
        on_delete=models.SET_DEFAULT, 
        default=Saga.get_default_pk, 
        related_name='arcs'  # 自定义反向关联名称
    )

class Clip(models.Model):
    # ... 其他字段
    arc = models.ForeignKey(
        Arc, 
        on_delete=models.SET_DEFAULT, 
        default=Arc.get_default_pk, 
        related_name='clips'  # 自定义反向关联名称
    )

之后模板中可以用更易读的名称遍历:

{% for saga in sagas %}
  <p>{{ saga.name }} has arcs {% for arc in saga.arcs.all %}{{ arc.name }}{% if not forloop.last %}, {% endif %}{% endfor %}</p>
  {% for arc in saga.arcs.all %}
    <p>&nbsp;&nbsp;&nbsp;&nbsp;{{ arc.name }} has clips {% for clip in arc.clips.all %}{{ clip.description }}{% if not forloop.last %} &amp; {% endif %}{% endfor %}</p>
  {% endfor %}
{% endfor %}

内容的提问来源于stack exchange,提问作者Makori SM

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 11:24:16