Chapter 05

模板 Template

Django 模板语言(DTL)负责把数据渲染成 HTML——变量、标签、过滤器、模板继承,一套简洁而克制的展示层语法。

5.1 DTL 概览

DTL(Django Template Language)刻意做得「弱逻辑」——它鼓励把复杂逻辑留在视图,模板只管展示。三个基本构件:变量、标签、过滤器。

DTL
Django 内置模板引擎,语法安全(自动 HTML 转义防 XSS)、故意限制逻辑能力,避免模板里塞业务代码。
变量(Variable)
{{ variable }},输出上下文中的值,支持点号访问属性/字典键/方法/列表索引。
标签(Tag)
{% tag %},控制逻辑与流程,如 {% if %}/{% for %}/{% url %}/{% block %}
过滤器(Filter)
{{ value|filter }},对变量做格式化,如 |upper/|date/|truncatewords/|default
模板继承(Inheritance)
{% extends %} + {% block %},用「骨架 + 覆盖块」复用布局,避免每页重复 header/footer。
上下文(Context)
视图传给模板的数据字典。render(request, tpl, {...}) 里的字典即上下文。
静态文件(Static)
CSS/JS/图片等不变资源,用 {% load static %} + {% static %} 生成 URL。
上下文处理器(Context Processor)
为所有模板注入公共变量的函数,如 requestuser,在 settings.TEMPLATES 配置。

5.2 变量、标签、过滤器

<h1>{{ post.title }}</h1>
<p>作者:{{ post.author.username|default:"匿名" }}</p>
<p>发布于 {{ post.created|date:"Y-m-d H:i" }}</p>
<p>{{ post.body|truncatewords:30 }}</p>

{% if post.published %}
  <span>已发布</span>
{% else %}
  <span>草稿</span>
{% endif %}

<ul>
{% for tag in post.tags.all %}
  <li>{{ forloop.counter }}. {{ tag.name }}</li>
{% empty %}
  <li>暂无标签</li>
{% endfor %}
</ul>

常用过滤器

过滤器作用
|upper / |lower大小写转换
|date:"Y-m-d"日期格式化
|default:"—"为空时的默认值
|truncatewords:20截断到 N 个词
|length长度
|linebreaks换行转 <p>/<br>
|safe标记为安全 HTML(不转义)

5.3 模板继承

<!-- templates/base.html:骨架 -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
  <title>{% block title %}古法编程{% endblock %}</title>
  <link rel="stylesheet" href="{% static 'css/site.css' %}">
</head>
<body>
  <header>导航...</header>
  <main>{% block content %}{% endblock %}</main>
  <footer>© 古法编程</footer>
</body>
</html>
<!-- templates/blog/list.html:继承并覆盖 -->
{% extends "base.html" %}

{% block title %}文章列表 · 古法编程{% endblock %}

{% block content %}
  {% for post in posts %}
    <a href="{% url 'blog:detail' post.pk %}">{{ post.title }}</a>
  {% endfor %}
{% endblock %}
DRY 的展示层

把公共布局放 base.html,各页面只覆盖 title/content 块。改导航、换 CSS 只需动一处——这就是模板继承的价值。

5.4 模板查找路径

# settings.py
TEMPLATES = [{
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "DIRS": [BASE_DIR / "templates"],   # 项目级模板目录
    "APP_DIRS": True,                     # 自动搜每个 App 的 templates/
    ...
}]
App 模板命名空间

APP_DIRS=True 时 Django 会搜所有 App 的 templates/,同名文件会冲突。惯例是再套一层 App 名:blog/templates/blog/list.html,引用时写 "blog/list.html"

5.5 静态文件

{% load static %}
<img src="{% static 'blog/logo.png' %}" alt="logo">
<script src="{% static 'blog/app.js' %}"></script>
# settings.py
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR / "static"]   # 开发期额外目录
STATIC_ROOT = BASE_DIR / "staticfiles"     # collectstatic 目标(生产)

5.6 自定义标签与过滤器

# blog/templatetags/blog_extras.py
from django import template
register = template.Library()

@register.filter
def badge(value):
    return "🔥" if value > 100 else ""

# 模板中:{% load blog_extras %} {{ post.views|badge }}

5.7 小结与下一步