5.1 模板语言 DTL 基础 本节摘要:Django 模板语言(DTL)由四类语法构成:变量输出、标签、过滤器与注释。本节逐一讲清用法与边界,重点解释自动转义如何充当安全底座、为什么模板语言故意不支持函数调用与算术。读懂设计意图,才不会用模板写出面条逻辑。 四类语法一张表 变量查找有顺序:先按字典键找,再按属性找,最后按下标找,任一命中即返回;全 miss 输出空字符串而不是报错——这是模板"容错优先"的性格,也是排查"页面为什么不显示"时要知道的第一件事:拼错变量名不会报错,只会安静地空白。 标签:控制结构三件套 if 支持 and、or、not 与比较运算,但刻意不支持复杂的表达式求值;for 只做遍历,empty 分支处理空集合。循环内可用的保留变量(forloop.
本节摘要:Django 模板语言(DTL)由四类语法构成:变量输出、标签、过滤器与注释。本节逐一讲清用法与边界,重点解释自动转义如何充当安全底座、为什么模板语言故意不支持函数调用与算术。读懂设计意图,才不会用模板写出面条逻辑。
{{ article.title }} 变量:输出上下文里的值 {% if articles %}...{% endif %} 标签:控制结构 {{ created_at|date:"Y年m月d日" }} 过滤器:管道式转换 {# 一行注释 #} 注释:渲染时丢弃
变量查找有顺序:先按字典键找,再按属性找,最后按下标找,任一命中即返回;全 miss 输出空字符串而不是报错——这是模板"容错优先"的性格,也是排查"页面为什么不显示"时要知道的第一件事:拼错变量名不会报错,只会安静地空白。
{% if user.is_authenticated %} 欢迎回来,{{ user.username }} {% elif show_login_tip %} 请先登录再评论 {% else %} <a>游客模式</a> {% endif %} {% for a in articles %} <article> <h3>{{ a.title }}</h3> <span>{{ a.created_at|date:"Y-m-d" }} · {{ a.author.username }}</span> </article> {% empty %} <p>还没有文章,等待第一篇。</p> {% endfor %} {% url 'blog:article-detail' article.pk as detail_url %} <a href="{{ detail_url }}">详情</a>
if 支持 and、or、not 与比较运算,但刻意不支持复杂的表达式求值;for 只做遍历,empty 分支处理空集合。循环内可用的保留变量(forloop.first、forloop.last、forloop.counter)足够覆盖斑马纹、分隔线这类常见需求。
第三段演示了 as 用法:标签结果存进变量,避免在长标签里嵌套引用。配合第 4 章的命名路由,模板里永远不出现硬编码地址。
过滤器用竖线串联,把值按需变形:
{{ article.body|truncatechars:100 }} 摘要截断 {{ article.body|safe }} 关闭转义(慎用) {{ comment.created_at|timesince }} 评论发布至今 3 天 {{ value|default:"未填写" }} 空值兜底 {{ list|join:"、" }} 拼接 {{ article.title|length }} 长度
有一个过滤器要单独敲黑板:safe。它关闭自动转义,把原始 HTML 放行。只有数据来源可信时才能用——比如后台编辑器产出的、经过白名单清洗的富文本。把用户评论标记为 safe,等于把 XSS 大门钥匙交给所有人。默认的转义行为(小于号变实体、引号变实体)正是第 1 章说的"框架替你挡的箭"之一。
⚠️ 模板语言没有加法、没有函数调用、不能随意访问下划线开头的属性。这些"缺陷"全是故意的:模板一旦能写业务逻辑,逻辑就会从视图层渗进来,最后模板没人敢动。缺计算能力时的正解是"在视图或模型层算好,把结果放进上下文"。
内置不够用时,在应用下建模板标签包即可扩展:
# 自定义过滤器示例:阅读量友好显示 from django import template register = template.Library() @register.filter(name="readable_views") def readable_views(value): if value >= 10000: return f"{value / 10000:.1f} 万" return str(value)
模板里就能用 {{ article.view_count|readable_views }}。写法约定固定:注册库实例、装饰器登记、纯函数实现。凡是"同一套格式化逻辑在多个模板重复"时,把它下沉为过滤器,而不是复制粘贴。
render 的第一个模板参数按配置的引擎查找:应用子目录优先、项目级目录兜底。墨迹博客的约定是每个应用的模板放自己的子目录(blog/list.html),全局共享的放项目级目录。同名冲突时按应用注册顺序取第一个——又一个"应用注册顺序影响行为"的例子,模板目录带应用前缀是最省心的避坑法。
引擎配置里有个值得认识的开关:字符串模板上下文处理器(context processors)让一批变量(比如 request、user)自动进入所有模板的上下文。为什么模板里总能用 user?答案就在默认配置的那几行处理器里——它们与第 4 章中间件配合,一个准备数据、一个供数入模。
最后补一个排查效率的工具:页面渲染结果与预期不符时,先在视图中打印传给模板的上下文字典,确认数据本身对不对;数据对而显示不对,再回头查模板语法。一半以上的"模板不生效"问题其实出在视图传参的名字拼写上——上下文键叫 articles、模板里写了 article_list,变量查找静默 miss,页面安静地空白。养成"先验数据、再查模板"的顺序,能省下大量对着模板发呆的时间。
语法只是砖,下一节砌墙:用继承体系把重复压到最少,并理顺静态与媒体两条文件线。