4、模板系统:模板语法、变量与过滤器、模板继承、静态文件加载、自定义过滤器

模板系统,说白了就是 Django 用来生成 HTML 的那套工具。你想想看,如果每个页面都手写死 HTML,那改个网站标题都得翻遍所有文件,多累啊。Django 的模板引擎就是来解决这个问题的——它让你把数据塞进 HTML 里,还能复用布局,省心不少。

我个人习惯把模板当成「带占位符的 HTML」。你写一个骨架,Django 帮你把肉填上去。嗯,咱们一步步来看。

4.1 模板语法:花括号里的秘密

Django 模板语法很简单,就三种花括号:

  • {{ 变量 }} —— 输出变量值
  • {% 标签 %} —— 执行逻辑(循环、判断等)
  • {# 注释 #} —— 注释,不会输出到页面

举个例子,你在视图里传了个 name 变量:

<h1>你好,{{ name }}!</h1>

如果 name = "张三",页面就会显示「你好,张三!」。就这么简单。

小提示:变量名支持点号访问属性,比如 {{ user.name }} 会先尝试字典键,再尝试属性,最后尝试列表索引。我刚开始学的时候老搞混,后来发现它自动帮你试,挺贴心的。

4.2 变量与过滤器:给数据化个妆

变量直接输出没问题,但有时候你想改改格式。比如日期太长了,想截短一点;或者字符串全大写。这时候过滤器就派上用场了。

过滤器的写法是 {{ 变量|过滤器 }},可以链式调用:

{{ my_date|date:"Y-m-d" }}
{{ my_text|upper|truncatewords:5 }}

常用的内置过滤器我列一下:

过滤器 作用 示例
date 格式化日期 {{ now|date:"Y-m-d H:i" }}
default 变量为空时显示默认值 {{ name|default:"访客" }}
length 返回列表/字符串长度 {{ items|length }}
safe 标记为安全 HTML,不转义 {{ html_content|safe }}
join 用指定字符连接列表 {{ list|join:", " }}
注意:Django 默认会转义所有 HTML 标签,防止 XSS 攻击。如果你确定内容是安全的,记得加 |safe。我曾经有个项目,用户提交的富文本内容没加 safe,结果所有 <b> 标签都显示成纯文本了,排查了半天。

4.3 模板继承:别再复制粘贴了

做网站最烦的是什么?每个页面都有导航栏、页脚、侧边栏,难道每个模板都写一遍?当然不。模板继承就是干这个的。

你写一个「父模板」,里面挖几个坑(block),子模板来填坑。比如 base.html

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
    <nav>...导航栏...</nav>
    <div id="content">
        {% block content %}{% endblock %}
    </div>
    <footer>...页脚...</footer>
</body>
</html>

子模板只需要写:

{% extends "base.html" %}

{% block title %}首页 - 我的网站{% endblock %}

{% block content %}
    <h1>欢迎来到首页</h1>
    <p>这是内容区域</p>
{% endblock %}

渲染出来,导航栏和页脚自动就有了,你只管填内容。我建议每个项目都搞一个 base.html,哪怕刚开始只有几个页面,后面扩展起来会非常舒服。

核心要点:
  • {% extends %} 必须放在子模板第一行
  • 父模板用 {% block 名称 %} 定义可覆盖区域
  • 子模板用同名 block 覆盖内容
  • 可以用 {{ block.super }} 引用父模板的 block 内容

4.4 静态文件加载:图片、CSS、JS 怎么放

模板里要引用 CSS、JS、图片,总不能写死路径吧?Django 提供了 {% static %} 标签来处理。

首先,在 settings.py 里配置:

STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']

然后在模板里:

{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}" alt="Logo">

注意第一行 {% load static %},这是告诉模板引擎我要用 static 标签。我见过不少新手忘了这行,结果路径死活加载不出来。

部署提醒:开发环境 Django 会自动处理静态文件。生产环境建议用 Nginx 或 CDN 来 serve 静态文件,再配合 collectstatic 命令把所有静态文件收集到一个目录。嗯,这个后面部署章节会细讲。

4.5 自定义过滤器:造个自己的工具

内置过滤器不够用?那就自己写。Django 允许你注册自定义过滤器,用法和内置的一模一样。

步骤很简单:

  1. 在 app 目录下建一个 templatetags 包(必须包含 __init__.py
  2. 在里面写一个 Python 文件,比如 my_filters.py
  3. 注册过滤器

举个例子,我想写一个把字符串变成「首字母大写其余小写」的过滤器:

# myapp/templatetags/my_filters.py
from django import template

register = template.Library()

@register.filter
def capitalize_first(value):
    """将字符串首字母大写,其余小写"""
    if not value:
        return value
    return value[0].upper() + value[1:].lower()

然后在模板里:

{% load my_filters %}
{{ "hello WORLD"|capitalize_first }}
{# 输出:Hello world #}

你还可以写带参数的过滤器:

@register.filter
def multiply(value, arg):
    return value * arg

{# 模板中使用:{{ 5|multiply:3 }} 输出 15 #}
避坑指南:我曾经写过一个过滤器,参数传了个字符串数字,结果死活算不对。后来发现过滤器参数默认是字符串,需要自己转类型。所以记得在过滤器函数里做类型转换,比如 int(arg)

嗯,模板系统这块基本就这些了。你想想看,变量输出、过滤器格式化、继承复用布局、静态文件加载、自定义扩展——这一套组合拳打下来,页面开发效率能翻好几倍。下一章咱们聊聊模型和数据库,那才是 Django 的重头戏。