Django 模板基础

Django 模板系统简介

Django 模板系统使用 Django 模板语言 (DTL),这是一种简单而强大的语言,专门设计用于在 HTML 中嵌入动态内容。模板系统允许开发者将 Python 代码与 HTML 分离,遵循 MVC (模型-视图-控制器) 或 MTV (模型-模板-视图) 设计模式。

视图 (View)

准备数据

模板 (Template)

渲染 HTML

用户

查看页面

模板语言特性
  • 变量 - 使用双花括号: {{ variable }}
  • 标签 - 使用花括号和百分号: {% tag %}
  • 过滤器 - 修改变量显示: {{ variable|filter }}
  • 注释 - 使用花括号和井号: {# comment #}
  • 模板继承 - 使用 {% extends %}{% block %}
模板文件位置

Django 在以下位置查找模板:

  1. 应用目录下的 templates 文件夹
  2. settings.pyDIRS 指定的目录
  3. 已安装应用的 templates 目录
# settings.py
TEMPLATES = [
    {
        'DIRS': [BASE_DIR / 'templates'],
        # ...
    },
]

模板变量

模板变量用于在模板中显示来自视图的上下文数据。变量使用双花括号 {{ }} 包围。

视图代码
views.py
from django.shortcuts import render

def user_profile(request):
    context = {
        'username': '张三',
        'age': 25,
        'hobbies': ['阅读', '编程', '旅行'],
        'profile': {
            'email': 'zhangsan@example.com',
            'joined': '2022-01-15'
        }
    }
    return render(request, 'profile.html', context)
模板代码
profile.html
<h1>用户资料</h1>
<p>用户名: @{{ username }}</p>
<p>年龄: {{ age }}</p>
<p>第一个爱好: {{ hobbies.0 }}</p>
<p>邮箱: {{ profile.email }}</p>
<p>注册日期: {{ profile.joined }}</p>

{# 使用 for 标签遍历列表 #}
<h3>爱好:</h3>
<ul>
{% for hobby in hobbies %}
    <li>{{ hobby }}</li>
{% endfor %}
</ul>
提示: 使用点号 . 访问变量的属性、字典键或列表索引。如果变量不存在,Django 默认会插入空字符串。

模板标签

模板标签提供了模板逻辑控制功能,使用 {% %} 语法。标签可以执行循环、条件判断、模板继承等操作。

常用标签示例

if/else 标签
条件判断
{% if user.is_authenticated %}
    <p>欢迎, {{ user.username }}!</p>
{% else %}
    <p>请<a href="/login/">登录</a></p>
{% endif %}

{% if age >= 18 %}
    <p>您是成年人</p>
{% elif age >= 13 %}
    <p>您是青少年</p>
{% else %}
    <p>您是儿童</p>
{% endif %}
for 标签
循环遍历
<ul>
{% for item in items %}
    <li>{{ forloop.counter }}: {{ item.name }}</li>
{% empty %}
    <li>没有项目</li>
{% endfor %}
</ul>

{# 遍历字典 #}
{% for key, value in data.items %}
    <p>{{ key }}: {{ value }}</p>
{% endfor %}
forloop 变量
变量 描述
forloop.counter 当前循环的索引(从1开始)
forloop.counter0 当前循环的索引(从0开始)
forloop.revcounter 从循环末尾开始的索引(从1开始)
forloop.revcounter0 从循环末尾开始的索引(从0开始)
forloop.first 如果是第一次循环,则为True
forloop.last 如果是最后一次循环,则为True
url 标签
生成 URL
{# 使用 URL 名称 #}
<a href="{% url 'home' %}">首页</a>

{# 带参数的 URL #}
<a href="{% url 'user_profile' user_id=user.id %}">用户资料</a>

{# 在视图和 URL 配置中的对应 #}
with 标签
缓存复杂变量
{# 缓存复杂查询结果 #}
{% with bio=user.profile.bio|default:"暂无简介" %}
    <p>{{ bio }}</p>
{% endwith %}

{% with total=products|length %}
    <p>共有 {{ total }} 个产品</p>
{% endwith %}

模板过滤器

过滤器用于修改变量的显示方式,使用管道符号 | 应用。可以串联多个过滤器。

常用过滤器示例
过滤器使用
{# 字符串过滤器 #}
<p>{{ name|lower }}</p>
<p>{{ title|upper }}</p>
<p>{{ message|title }}</p>
<p>{{ text|truncatewords:10 }}</p>

{# 日期过滤器 #}
<p>{{ pub_date|date:"Y-m-d" }}</p>
<p>{{ created_at|timesince }}</p>

{# 数字过滤器 #}
<p>{{ price|floatformat:2 }}</p>
<p>{{ items|length }}</p>

{# 默认值过滤器 #}
<p>{{ description|default:"暂无描述" }}</p>

{# 安全过滤器 #}
<p>{{ html_content|safe }}</p>
常用过滤器参考
过滤器 描述 示例
lower 转换为小写 {{ "HELLO"|lower }} → "hello"
upper 转换为大写 {{ "hello"|upper }} → "HELLO"
length 返回长度 {{ list|length }} → 列表长度
default 默认值 {{ value|default:"空" }}
date 日期格式化 {{ date|date:"Y-m-d" }}
truncatechars 截断字符 {{ text|truncatechars:20 }}
floatformat 浮点数格式化 {{ 3.14159|floatformat:2 }} → 3.14
safe 标记为安全HTML {{ html|safe }}
注意: 使用 safe 过滤器时要特别小心,确保内容是可信的,否则可能导致 XSS 攻击。

模板继承

模板继承是 Django 模板系统最强大的功能之一。它允许您创建一个基础模板,然后在子模板中覆盖特定部分。

基础模板
base.html
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}我的网站{% endblock %}</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <div id="header">
        <h1>{% block header %}网站标题{% endblock %}</h1>
    </div>

    <div id="content">
        {% block content %}
        <p>默认内容</p>
        {% endblock %}
    </div>

    <div id="footer">
        {% block footer %}
        <p>© 2023 我的网站</p>
        {% endblock %}
    </div>
</body>
</html>
子模板
about.html
{% extends "base.html" %}

{% block title %}关于我们 - 我的网站{% endblock %}

{% block header %}
    <h1>关于我们</h1>
{% endblock %}

{% block content %}
    <h2>公司简介</h2>
    <p>我们是一家专注于...</p>

    {# 引用父模板的内容 #}
    {{ block.super }}
{% endblock %}

{% block footer %}
    <p>© 2023 我的网站 - 关于页面</p>
{% endblock %}
include 标签
包含其他模板
{# 包含导航栏 #}
{% include "navbar.html" %}

{# 包含带变量的模板 #}
{% include "product_item.html" with product=item %}

{# 只使用本地变量 #}
{% include "name_snippet.html" with greeting="Hello" only %}

自定义过滤器和标签

除了内置的过滤器和标签,Django 还允许您创建自定义过滤器和标签。

自定义过滤器
templatetags/custom_filters.py
from django import template

register = template.Library()

@register.filter
def multiply(value, arg):
    """将值乘以参数"""
    try:
        return float(value) * float(arg)
    except (ValueError, TypeError):
        return ''

@register.filter
def replace_comma(value):
    """将逗号替换为空格"""
    return str(value).replace(',', ' ')
在模板中使用
模板文件
{% load custom_filters %}

<p>价格: {{ price|multiply:1.1 }}</p>
<p>{{ tags|replace_comma }}</p>
自定义标签
templatetags/custom_tags.py
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    from datetime import datetime
    return datetime.now().strftime(format_string)

@register.inclusion_tag('results.html')
def show_results(poll):
    choices = poll.choice_set.all()
    return {'choices': choices}

模板最佳实践

组织模板结构
  • 使用有意义的模板名称
  • 合理组织模板目录结构
  • 为可重用组件创建模板片段
  • 使用模板继承减少重复代码
templates/
├── base.html
├── includes/
│   ├── header.html
│   ├── footer.html
│   └── sidebar.html
├── accounts/
│   ├── login.html
│   └── profile.html
└── products/
    ├── list.html
    └── detail.html
性能优化
  • 避免在模板中进行复杂计算
  • 使用 {% with %} 缓存复杂查询
  • 合理使用 {% include %} 标签
  • 考虑使用模板片段缓存
{# 使用 with 缓存查询 #}
{% with count=users.count %}
    <p>用户数: {{ count }}</p>
{% endwith %}

{# 模板片段缓存 #}
{% load cache %}
{% cache 500 sidebar %}
    {# 侧边栏内容 #}
{% endcache %}