4.1 Django 模板语言 (DTL) 基础


文档摘要

第四章:模板(Templates) 4.1 Django 模板语言(DTL)基础 Django 模板语言(Django Template Language,DTL)是 Django 框架的核心表现层技术,专为安全、高效地将动态数据注入 HTML、XML 或纯文本文档而设计。其核心理念在于在强大功能与开发简洁性之间取得平衡,支持前后端职责清晰分离,显著提升团队协作效率与代码可维护性。 4.1.1 模板的概念与作用 传统 Web 开发中,HTML 结构与动态逻辑常混杂于同一文件,导致可读性差、复用率低、安全风险高。

第四章:模板(Templates)

4.1 Django 模板语言(DTL)基础

Django 模板语言(Django Template Language,DTL)是 Django 框架的核心表现层技术,专为安全、高效地将动态数据注入 HTML、XML 或纯文本文档而设计。其核心理念在于在强大功能与开发简洁性之间取得平衡,支持前后端职责清晰分离,显著提升团队协作效率与代码可维护性。

4.1.1 模板的概念与作用

传统 Web 开发中,HTML 结构与动态逻辑常混杂于同一文件,导致可读性差、复用率低、安全风险高。Django 模板机制通过严格分离表现层(HTML/CSS/JS)与业务逻辑层(Python),践行“关注点分离”(Separation of Concerns)原则,成为现代 Web 开发的最佳实践之一。

模板本质是纯文本文件,可为 HTML、XML、CSV、纯文本甚至电子邮件模板。其内容由两部分构成:

  • 静态内容:原始 HTML 标签、CSS 类名、文案等;
  • 动态占位符:遵循 DTL 语法规则的变量、标签与过滤器,由模板引擎在渲染时动态替换为实际数据。

模板的核心价值

价值维度 具体体现
前后端解耦 前端专注 UI/UX 实现与样式优化;后端专注数据建模与业务逻辑;双方通过明确定义的上下文(Context)契约协作
高复用性 单个模板可被多个视图复用;支持模板继承({% extends %})、模板包含({% include %})与宏定义(自定义标签/过滤器),避免重复编码
结构清晰性 相比 Python 字符串拼接 HTML,DTL 语法天然贴近 HTML 语义,大幅提升模板可读性与协作效率
内置安全性 默认启用 HTML 自动转义,有效防御 XSS(跨站脚本)攻击;关键操作(如表单提交)强制 CSRF 防护

模板渲染流程

用户请求进入 Django 后,经历以下标准化渲染链路:

图 4.1.1 模板渲染流程
视图函数负责数据组装与业务判断,模板引擎专注视图渲染——二者职责分明,共同保障系统健壮性与可扩展性。

4.1.2 DTL 语法基础

DTL 由四大原子组件构成,共同支撑动态模板能力:

组件 语法形式 用途 示例
变量(Variables) {{ variable }} 输出上下文数据 {{ user.name }}, `{{ article.pub_date
标签(Tags) {% tag %}...{% endtag %} 控制逻辑(条件、循环、URL、安全等) {% if user.is_authenticated %}, {% for post in posts %}
过滤器(Filters) {{ variable|filter }} 格式化变量输出 {{ title|upper }}, {{ content|truncatewords:30 }}
注释(Comments) {# comment #} 模板内说明,不输出至前端 {# 用户头像区域,仅登录用户可见 #}

4.1.2.1 变量(Variables)

变量是 DTL 最基础的数据输出单元,使用双大括号 {{ }} 包裹,语法简洁且语义明确。

变量查找遵循三级优先级规则:

  1. 字典键查找{{ user.profile.bio }}user['profile']['bio']
  2. 对象属性访问{{ article.author.get_full_name }} → 调用 get_full_name() 方法
  3. 列表索引访问{{ skills.0 }} → 等价于 skills[0]

最佳实践:避免深层嵌套(如 {{ a.b.c.d.e }}),应在视图中预处理数据,提升模板可读性与性能。

示例:上下文数据与模板输出

视图传递上下文:

def profile_view(request): context = { 'name': '张三', 'age': 30, 'city': '北京', 'skills': ['Python', 'Django', 'HTML', 'CSS'], 'address': {'street': '科技路', 'zip_code': '100000'} } return render(request, 'profile.html', context)

对应模板 profile.html

<h1>{{ name }} 的个人资料</h1> <p>年龄:{{ age }} 岁</p> <p>所在地:{{ city }}</p> <p>技能:{{ skills|join:", " }}</p> <p>详细地址:{{ address.street }}(邮编:{{ address.zip_code }})</p>

渲染结果:

<h1>张三 的个人资料</h1> <p>年龄:30 岁</p> <p>所在地:北京</p> <p>技能:Python, Django, HTML, CSS</p> <p>详细地址:科技路(邮编:100000)</p>

4.1.2.2 标签(Tags)

标签承载控制流与框架集成能力,是 DTL 的“逻辑引擎”。

条件判断:{% if %}{% elif %}{% else %}

支持完整布尔表达式,兼容 andornotin==!= 等运算符。

{% if user.is_authenticated %} <p>欢迎回来,{{ user.username }}!</p> {% if user.is_staff %} <a href="{% url 'admin:index' %}">进入管理后台</a> {% endif %} {% else %} <a href="{% url 'login' %}">请登录</a> {% endif %}
循环遍历:{% for %}{% empty %}

支持可迭代对象(QuerySet、list、dict),内置 forloop 变量提供循环元信息。

<ul class="article-list"> {% for article in articles %} <li class="{% if forloop.first %}first{% elif forloop.last %}last{% endif %}"> <h3><a href="{% url 'article_detail' article.id %}">{{ article.title|title }}</a></h3> <time datetime="{{ article.pub_date|date:'c' }}">发布于 {{ article.pub_date|date:"Y年m月d日" }}</time> <p>{{ article.excerpt|default:article.content|truncatewords:25 }}</p> </li> {% empty %} <li class="no-content">暂无文章,敬请期待。</li> {% endfor %} </ul>
安全与集成标签
标签 用途 关键说明
{% csrf_token %} 表单防伪造 所有 POST 表单必需,Django 中间件自动校验
{% url 'name' arg1 arg2 %} URL 反向解析 解耦硬编码路径,支持命名空间与参数化路由
{% static 'css/main.css' %} 静态资源定位 依赖 STATIC_URL 配置,支持 CDN 扩展
{% load humanize %} 加载扩展库 启用 intcommanaturaltime 等人性化过滤器

⚠️ 安全警示{% url %}{% static %} 标签自动进行 URL 编码,避免 XSS 风险;手动拼接 URL(如 href="/articles/{{ id }}/")属反模式。

4.1.2.3 过滤器(Filters)

过滤器是 DTL 的“数据加工流水线”,通过管道符 | 链式调用,实现轻量级格式化。

常用内置过滤器分类
类别 过滤器 示例 输出
文本处理 lower / upper / title / capfirst `{{ "hello WORLD" title }}`
日期时间 date:"Y-m-d" / time:"H:i" / timesince `{{ post.pub_date timesince }}`
截断与长度 truncatechars:50 / truncatewords:10 / length `{{ text truncatewords:8 }}`
HTML 安全 safe / escape / linebreaks `{{ html_content safe }}`

高级用法示例:

<!-- 链式过滤:先截断再转义,再添加省略号 --> <p>{{ article.content|truncatewords:20|escape }}…</p> <!-- 带参数的过滤器 --> <p>阅读时长:{{ article.content|wordcount|add:"0" }} 字,约 {{ article.content|wordcount|divisibleby:200 }} 分钟</p> <!-- 人性化时间显示(需 {% load humanize %}) --> <time>{{ article.pub_date|naturaltime }}</time> <!-- "2 分钟前" -->

性能提示:过滤器在模板渲染时实时执行,避免在模板中调用耗时方法(如数据库查询);复杂计算应在视图或模型中完成。

4.1.2.4 注释(Comments)

DTL 注释 {# ... #} 仅存在于模板源码中,完全不会输出至 HTML,是提升团队协作效率的关键工具。

{# 用户资料卡片:包含头像、昵称、认证状态 #} <div class="user-card"> {# 头像区域 —— 优先展示 Gravatar,fallback 为默认图标 #} <img src="{{ user.email|gravatar:80 }}" alt="{{ user.get_full_name }}" width="80" height="80"> <h2>{{ user.get_full_name|default:"匿名用户" }}</h2> {# 认证徽章:仅对已验证邮箱用户显示 #} {% if user.profile.email_verified %} <span class="badge verified">✓ 已验证</span> {% endif %} </div>

4.1.3 实战:构建博客文章列表页

以下通过一个完整可运行的博客列表页面,串联 DTL 核心语法,强化工程化认知。

步骤 1:模型定义(blog/models.py

from django.db import models from django.utils import timezone class Article(models.Model): title = models.CharField('标题', max_length=200) slug = models.SlugField('URL别名', unique=True, blank=True) content = models.TextField('正文') excerpt = models.CharField('摘要', max_length=300, blank=True) pub_date = models.DateTimeField('发布日期', default=timezone.now) is_published = models.BooleanField('是否发布', default=True) class Meta: verbose_name = '文章' verbose_name_plural = '文章' ordering = ['-pub_date'] def __str__(self): return self.title

步骤 2:视图逻辑(blog/views.py

from django.shortcuts import render from .models import Article def article_list(request): articles = Article.objects.filter( is_published=True ).select_related('author').only( 'title', 'slug', 'excerpt', 'pub_date' ).order_by('-pub_date') return render(request, 'blog/article_list.html', { 'articles': articles, 'page_title': '最新文章' })

步骤 3:模板实现(templates/blog/article_list.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>{{ page_title }} - {{ SITE_NAME|default:"我的博客" }}</title> <link rel="stylesheet" href="{% static 'css/blog.css' %}"> </head> <body> <header class="site-header"> <h1>{{ page_title }}</h1> <nav> <a href="{% url 'home' %}">首页</a> <a href="{% url 'about' %}">关于</a> </nav> </header> <main class="article-container"> {% if articles %} <ul class="article-list"> {% for article in articles %} <li class="article-item"> <article> <header> <h2> <a href="{% url 'article_detail' article.slug %}"> {{ article.title|truncatewords:8 }} </a> </h2> <time datetime="{{ article.pub_date|date:'c' }}"> {{ article.pub_date|date:"Y年m月d日" }} </time> </header> <div class="excerpt"> {% if article.excerpt %} {{ article.excerpt|linebreaks }} {% else %} {{ article.content|truncatewords:30|linebreaks }} {% endif %} </div> <footer> <a href="{% url 'article_detail' article.slug %}" class="read-more"> 阅读全文 → </a> </footer> </article> </li> {% endfor %} </ul> {% else %} <div class="no-content"> <h2>暂无文章</h2> <p>作者正在努力创作中,敬请期待更新。</p> </div> {% endif %} </main> <footer class="site-footer"> <p>&copy; {% now "Y" %} {{ SITE_NAME|default:"我的博客" }}. 保留所有权利。</p> </footer> </body> </html>

步骤 4:URL 配置(blog/urls.py

from django.urls import path from . import views app_name = 'blog' urlpatterns = [ path('', views.article_list, name='article_list'), path('article/<slug:slug>/', views.article_detail, name='article_detail'), ]

关键实践亮点

  • 使用 select_related/only 优化数据库查询
  • slug 字段支持 SEO 友好 URL
  • truncatewords + linebreaks 实现安全摘要渲染
  • now 模板标签动态生成版权年份
  • app_name 支持命名空间化 URL 反向解析

4.1.4 总结与进阶指引

Django 模板语言(DTL)并非简单字符串替换工具,而是融合安全性设计、工程化约束与开发者体验的成熟模板系统。本节核心要点总结如下:

  • 模板即契约:定义清晰的上下文接口,是前后端高效协作的技术基石;
  • 语法即规范:变量、标签、过滤器、注释四组件覆盖 95% 动态渲染场景;
  • 安全即默认:自动转义、CSRF 保护、URL 编码等机制内置于核心,降低安全盲区;
  • 实践即验证:博客列表页示例完整覆盖数据获取、循环渲染、条件判断、URL 生成、静态资源引用等关键链路。

后续学习路径建议:

  • 模板继承:通过 {% extends %}{% block %} 构建可复用页面骨架
  • 自定义标签与过滤器:用 Python 扩展 DTL,封装业务逻辑(如 Markdown 渲染、权限判断)
  • 模板调试技巧django-debug-toolbar 模板面板、{% debug %} 标签、上下文变量检查
  • 性能优化{% cache %} 模板缓存、select_related/prefetch_related 查询优化

掌握 DTL 基础,是构建可维护、可扩展、高安全 Django 应用的必经之路。从今日起,让每一个模板都成为清晰、健壮、富有表现力的前端契约。


发布者: 作者: 转发
评论区 (0)
U