第四章:模板 (Templates)


文档摘要

第四章:Django 模板系统(Templates) 核心摘要:Django 模板系统是 MTV 架构中实现“展示层”的关键组件,通过变量、标签、过滤器与继承机制,实现逻辑与视图的彻底解耦,兼顾安全性、可维护性与开发效率。本章系统讲解模板语法、内置功能、继承体系、自定义扩展及生产级最佳实践。 4.1 模板的作用与优势 在传统 Web 开发中,HTML 与业务逻辑混杂导致代码臃肿、协作低效、安全风险高。Django 采用 模型-模板-视图(MTV) 设计模式,将数据层(Model)、展示层(Template)与控制层(View)严格分离。模板层专司用户界面渲染——接收视图传入的数据上下文(Context),生成最终的 HTML 响应,不执行任何数据库操作或复杂计算。

第四章:Django 模板系统(Templates)

核心摘要:Django 模板系统是 MTV 架构中实现“展示层”的关键组件,通过变量、标签、过滤器与继承机制,实现逻辑与视图的彻底解耦,兼顾安全性、可维护性与开发效率。本章系统讲解模板语法、内置功能、继承体系、自定义扩展及生产级最佳实践。

4.1 模板的作用与优势

在传统 Web 开发中,HTML 与业务逻辑混杂导致代码臃肿、协作低效、安全风险高。Django 采用 模型-模板-视图(MTV) 设计模式,将数据层(Model)、展示层(Template)与控制层(View)严格分离。模板层专司用户界面渲染——接收视图传入的数据上下文(Context),生成最终的 HTML 响应,不执行任何数据库操作或复杂计算。

其核心优势体现在以下五方面:

  • 关注点分离(Separation of Concerns)
    模板仅处理展示逻辑,Python 代码专注业务规则。前端工程师可独立修改 HTML/CSS,后端开发者专注数据处理,大幅提升跨职能协作效率。

  • 高度可复用(Code Reusability)
    依托模板继承({% extends %})与片段包含({% include %}),公共结构(如页头、导航、页脚)只需编写一次,全站复用。

  • 默认安全防护(Security by Default)
    所有变量输出自动进行 HTML 转义,有效防御跨站脚本攻击(XSS)。仅当明确信任内容来源时,才需使用 |safe 过滤器解除转义。

  • 卓越可读性与可维护性(Readability & Maintainability)
    模板语法简洁直观,非程序员亦可理解基础结构;逻辑清晰、层级分明,显著降低长期维护成本。

  • 灵活可扩展(Flexibility)
    支持自定义模板标签与过滤器,可无缝集成复杂业务逻辑(如权限控制、时间计算、富文本处理),满足企业级应用需求。

4.2 模板基础语法

Django 模板语言(DTL)是一套专为展示层设计的轻量级、安全的模板引擎,并非 Python 代码,拥有独立语法规则,包含四大核心元素:

元素 语法格式 作用说明
变量 {{ variable }} 渲染视图传入的数据,自动 HTML 转义
标签 {% tag %} 控制模板逻辑(条件、循环、继承、URL 解析等)
过滤器 {{ variable|filter }} 修改变量显示格式(大小写、截断、日期格式化等)
注释 {# comment #} 模板内说明性文字,不输出到 HTML,不被解析

实战示例:用户列表渲染

视图层(views.py

from django.shortcuts import render def user_list_view(request): context = { 'page_title': '用户管理', 'users': [ {'name': 'Alice', 'role': 'admin', 'joined': '2023-01-15'}, {'name': 'Bob', 'role': 'editor', 'joined': '2023-03-22'}, {'name': 'Charlie', 'role': 'viewer', 'joined': '2023-05-10'} ] } return render(request, 'users/list.html', context)

模板层(templates/users/list.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>{{ page_title }}</title> </head> <body> <header> <h1>{{ page_title }}</h1> </header> <main> <p>共 {{ users|length }} 位用户:</p> <ul> {% for user in users %} <li> <strong>{{ user.name|title }}</strong> ({{ user.role|upper }}|加入于 {{ user.joined|date:"Y年m月d日" }}) </li> {% empty %} <li>暂无用户数据</li> {% endfor %} </ul> </main> </body> </html>

关键要点

  • {{ page_title }} 直接渲染字符串;
  • |title|upper 为链式过滤器,提升可读性;
  • {% for %} 内置 {% empty %} 处理空列表场景;
  • {{ user.joined|date:"Y年m月d日" }} 实现本地化日期格式。

4.3 模板标签详解

DTL 标签是模板逻辑控制的核心,分为三类:控制流标签变量操作标签功能型标签

4.3.1 控制流标签

条件判断:{% if %} 系列

{% 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 %} 与循环变量

{% for post in latest_posts %} <article class="{% if forloop.first %}featured{% endif %}"> <h2>{{ post.title }}</h2> <time>{{ post.pub_date|date:"M d, Y" }}</time> <p>{{ post.excerpt|truncatewords:30 }}</p> {% if not forloop.last %}<hr>{% endif %} </article> {% empty %} <p>暂无文章发布。</p> {% endfor %}

循环变量说明
forloop.first / lastcounter(1起)、counter0(0起)、revcounter(倒序)、parentloop(嵌套循环)。

4.3.2 变量操作与功能标签

标签 用途 示例
{% with %} 创建局部作用域,简化复杂表达式 {% with total=order.items.count %}共{{ total }}件{% endwith %}
{% csrf_token %} 生成防 CSRF 令牌(POST 表单必需 <form method="post">{% csrf_token %}...
{% url 'name' arg1 arg2 %} 反向解析 URL,避免硬编码 <a href="{% url 'article_detail' pk=article.id %}">{{ article.title }}</a>
{% static 'path' %} 生成静态资源 URL(CSS/JS/图片) <img src="{% static 'images/logo.svg' %}" alt="Logo">
{% include 'partial.html' %} 复用模板片段(推荐用于组件化) {% include 'components/navbar.html' %}

⚠️ 重要配置:使用 {% static %} 前,模板顶部需声明 {% load static %};项目需配置 STATIC_URLSTATICFILES_DIRS

4.3.3 模板继承:{% extends %}{% block %}

模板继承是构建可维护网站的基石。通过定义基础模板(base.html),各子模板仅覆盖必要区块,确保全局风格统一。

基础模板(templates/base.html

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{% block title %}MySite{% endblock %} - Django 应用</title> <link rel="stylesheet" href="{% static 'css/base.css' %}"> {% block extra_css %}{% endblock %} </head> <body> <header>{% block header %}<h1>MySite</h1>{% endblock %}</header> <main class="container">{% block content %}{% endblock %}</main> <footer>{% block footer %}<p>&copy; 2024 MySite</p>{% endblock %}</footer> <script src="{% static 'js/base.js' %}"></script> {% block extra_js %}{% endblock %} </body> </html>

子模板(templates/articles/list.html

{% extends 'base.html' %} {% load static %} {% block title %}文章列表 - {{ block.super }}{% endblock %} {% block extra_css %} <link rel="stylesheet" href="{% static 'css/articles.css' %}"> {% endblock %} {% block header %} <nav> <a href="{% url 'home' %}">首页</a> <a href="{% url 'articles:list' %}">所有文章</a> </nav> <h1>最新文章</h1> {% endblock %} {% block content %} <section class="articles-grid"> {% for article in articles %} <article> <h2><a href="{% url 'articles:detail' article.pk %}">{{ article.title|truncatechars:50 }}</a></h2> <p class="meta">{{ article.author }} • {{ article.pub_date|date:"Y-m-d" }}</p> <p>{{ article.summary|truncatewords:20 }}</p> </article> {% endfor %} </section> {% endblock %} {% block extra_js %} <script src="{% static 'js/articles.js' %}"></script> {% endblock %}

继承机制要点

  • {% extends %} 必须是模板第一行;
  • {% block.super %} 复用父模板内容,实现增量覆盖;
  • 未被子模板覆盖的 block(如 footer)自动继承父模板内容。

4.4 模板过滤器详解

过滤器是模板中数据格式化的“管道”,支持链式调用,语法为 {{ variable|filter1|filter2:param }}

常用内置过滤器分类速查表

类别 过滤器 说明 示例
字符串处理 lower, upper, title, capfirst 大小写转换 `{{ "hello"
truncatechars:10, truncatewords:5 截断内容 `{{ "Hello world!"
urlencode, urlize, striptags URL/HTML 安全处理 `{{ "

Hello

"
数字处理 intcomma, floatformat:2, add:"5" 格式化与计算 `{{ 1000000
日期时间 date:"Y-m-d", time:"H:i", timesince 时间格式化与相对时间 `{{ post.pub_date
列表处理 first, last, join:", ", random 列表操作 `{{ items
HTML 安全 safe, escape, linebreaks 渲染控制 `{{ raw_html
默认值 default:"N/A", default_if_none:"—" 空值兜底 `{{ user.nickname

过滤器链式应用(典型场景)

<!-- 安全渲染文章摘要:去HTML标签 → 截断200字符 → 换行转<br> --> {{ article.content|striptags|truncatechars:200|linebreaks }} <!-- 显示作者头像:空则用默认图,URL 编码防止特殊字符问题 --> <img src="{% static 'images/avatars/'|add:article.author.avatar|urlencode %}" alt="{{ article.author.name }}">

4.5 自定义模板标签与过滤器

当内置功能无法满足业务需求时,可通过 Python 编写自定义逻辑。

创建步骤(标准流程)

  1. 在应用目录下创建 templatetags/ 子目录(含 __init__.py);
  2. 新建 Python 文件(如 blog_tags.py);
  3. 使用 @register.filter@register.simple_tag 注册;
  4. 模板中 {% load blog_tags %} 加载使用。

示例 1:自定义过滤器 —— 中文日期格式化

myapp/templatetags/date_filters.py

from django import template from django.utils import timezone from django.utils.translation import gettext as _ register = template.Library() @register.filter def cn_date(value, format_string="Y年m月d日"): """中文日期格式化过滤器""" if not value: return "" # 转换为本地时区 local_time = timezone.template_localtime(value) return local_time.strftime(format_string).replace("Y", str(local_time.year))

模板中使用

{% load date_filters %} <p>发布时间:{{ article.pub_date|cn_date }}</p> <!-- 输出:2024年06月15日 -->

示例 2:自定义简单标签 —— 权限快捷判断

myapp/templatetags/auth_tags.py

from django import template from django.contrib.auth.decorators import login_required register = template.Library() @register.simple_tag(takes_context=True) def user_can_edit(context, obj): """判断当前用户是否有编辑对象权限""" request = context['request'] return request.user.has_perm(f'{obj._meta.app_label}.change_{obj._meta.model_name}') and request.user.is_authenticated

模板中使用

{% load auth_tags %} {% user_can_edit article as can_edit %} {% if can_edit %} <a href="{% url 'articles:edit' article.pk %}">编辑</a> {% endif %}

最佳实践

  • 过滤器用于数据转换(无副作用);
  • 简单标签用于生成内容(如当前时间、权限检查);
  • 包含标签(@register.inclusion_tag)用于渲染子模板(如分页组件)。

4.6 模板加载与渲染机制

模板渲染由 render() 函数驱动,其底层依赖 Django 的模板引擎配置与查找策略。

render() 函数标准用法

from django.shortcuts import render def product_detail(request, pk): product = get_object_or_404(Product, pk=pk) context = { 'product': product, 'related_products': Product.objects.filter(category=product.category)[:4], 'is_in_wishlist': request.user.is_authenticated and product in request.user.wishlist.all() } return render(request, 'products/detail.html', context)

模板路径配置(settings.py

import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent.parent TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ BASE_DIR / 'templates', # 项目级模板(全局复用) BASE_DIR / 'apps/core/templates', # 核心应用模板 ], 'APP_DIRS': True, # 自动扫描各 app/templates/ 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.static', # 自定义处理器(如站点信息) 'myproject.context_processors.site_info', ], }, }, ]

模板加载优先级

  1. DIRS 中从左到右依次查找;
  2. 若未找到,扫描已注册应用的 templates/ 目录(APP_DIRS=True);
  3. 同名模板以最先找到者为准(可被子应用覆盖父模板)。

4.7 模板最佳实践与安全准则

原则 推荐做法 反例
逻辑分离 视图处理数据、权限、业务规则;模板仅负责渲染 在模板中调用 user.profile.get_full_name() 等复杂方法
继承优先 全站统一 base.html,按模块拆分子模板(articles/base.html, users/base.html 每个页面独立编写完整 HTML 结构
URL 与静态资源 100% 使用 {% url %}{% static %} <a href="/articles/123/">, <img src="/static/logo.png">
XSS 防护 默认信任转义,仅对可信富文本使用 ` safe`
目录结构 按应用组织:templates/<app_name>/<feature>/xxx.html 所有模板平铺在 templates/
性能优化 避免模板中数据库查询(N+1 问题);预取关联数据(select_related, prefetch_related {% for comment in post.comment_set.all %}(未预取)
可测试性 为关键模板编写单元测试,验证上下文与渲染结果 仅依赖手动浏览器测试

4.8 总结:构建可维护的模板体系

Django 模板系统远不止是“填空式 HTML 生成器”。它是一套安全、分层、可扩展的展示架构

  • MTV 解耦:将 UI 渲染从 Python 逻辑中剥离,使团队协作更高效;
  • 继承与包含:通过 base.html + block + include 构建可复用的 UI 组件库;
  • 安全默认:自动转义、CSRF 令牌、沙箱化标签,筑牢 Web 安全第一道防线;
  • 灵活扩展:自定义过滤器与标签,无缝对接复杂业务场景;
  • 工程化支撑:标准化路径配置、上下文处理器、静态资源管理,支撑规模化交付。

掌握本章内容,意味着您已具备构建专业级 Django Web 应用界面的完整能力。后续章节将深入模型关系、表单处理、用户认证与 REST API 开发,逐步构建功能完备的企业级应用。

关键词:Django 模板、MTV 架构、模板继承、Django 模板标签、Django 过滤器、{% url %}{% static %}、自定义模板标签、XSS 防护、模板最佳实践


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