Django Template模板层详解
Django作为一个功能强大的Web框架,其模板系统是构建动态网页不可或缺的一部分。今天,我们将深入探讨Django模板系统的工作原理、语法特性以及高级用法,帮助你快速掌握这一核心技能。
课程目标
- 理解Django模板系统的工作原理
- 掌握模板语法和标签的使用
- 学会创建和使用自定义模板标签和过滤器
- 了解模板继承和包含机制
模板系统概述
Django模板系统是Django MTV架构中的"T"层(Template),负责将数据渲染成HTML或其他格式的输出。模板系统提供了一种简洁而强大的语法,让设计师和开发者能够协作构建动态网页。
模板系统的组成:
- 模板:包含占位符和标签的文本文件
- 上下文:传递给模板的数据
- 渲染:将上下文数据填充到模板中的过程
模板基础语法
变量
模板变量用于显示从视图传递过来的数据。变量名由字母、数字、下划线和点组成,使用双大括号包围。
<!-- 在模板中显示变量 -->
<p>欢迎,{{ name }}!</p>
<p>今天的日期是 {{ date }}</p>
<!-- 访问对象属性 -->
<p>用户名:{{ user.username }}</p>
<p>邮箱:{{ user.email }}</p>
<!-- 访问字典值 -->
<p>城市:{{ profile.address.city }}</p>
过滤器
过滤器用于修改变量的显示格式,使用管道符 | 来应用过滤器,多个过滤器可以链式调用。
<!-- 过滤器用于格式化变量 -->
<p>{{ name|upper }}</p> <!-- 转为大写 -->
<p>{{ content|truncatewords:30 }}</p> <!-- 截断为30个单词 -->
<p>{{ price|floatformat:2 }}</p> <!-- 保留两位小数 -->
<p>{{ date|date:"Y-m-d H:i:s" }}</p> <!-- 格式化日期 -->
<!-- 链式过滤器 -->
<p>{{ content|striptags|truncatewords:50 }}</p>
模板标签
模板标签由 {% 和 %} 包围,用于执行逻辑操作,如循环、条件判断等。
控制结构标签
if/elif/else标签
条件标签允许你根据条件显示不同的内容。
{% if user.is_authenticated %}
<p>欢迎回来,{{ user.username }}!</p>
{% elif user.is_anonymous %}
<p>请登录</p>
{% else %}
<p>未知用户</p>
{% endif %}
<!-- 使用比较运算符 -->
{% if articles|length > 5 %}
<p>文章数量较多</p>
{% endif %}
for循环标签
for标签用于遍历序列,还提供了一系列有用的循环变量。
<!-- 基本for循环 -->
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% empty %}
<li>暂无文章</li>
{% endfor %}
</ul>
<!-- 带索引的循环 -->
{% for article in articles %}
<p>{{ forloop.counter }}. {{ article.title }}</p>
{% endfor %}
常用forloop变量:
forloop.counter: 从1开始的计数器
forloop.counter0: 从0开始的计数器
forloop.revcounter: 反向计数器
forloop.first: 是否是第一个元素
forloop.last: 是否是最后一个元素
模板继承
模板继承是Django模板系统最强大的部分之一,它允许你创建一个基础"骨架"模板,包含站点的所有共同元素,并定义子模板可以覆盖的块。
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}默认标题{% endblock %}</title>
{% block extra_head %}{% endblock %}
</head>
<body>
<header>
{% block header %}
<h1>我的网站</h1>
{% endblock %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}
<p>© 2023 版权所有</p>
{% endblock %}
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>
<!-- article_list.html -->
{% extends "base.html" %}
{% block title %}文章列表 - 我的博客{% endblock %}
{% block content %}
<h2>文章列表</h2>
{% for article in articles %}
<article>
<h3><a href="{% url 'article_detail' article.id %}">{{ article.title }}</a></h3>
<p>{{ article.summary|truncatewords:30 }}</p>
<p class="meta">发布于 {{ article.pub_date|date:"Y-m-d" }}</p>
</article>
{% empty %}
<p>暂无文章</p>
{% endfor %}
<!-- 分页 -->
{% if articles.has_other_pages %}
<div class="pagination">
{% if articles.has_previous %}
<a href="?page={{ articles.previous_page_number }}">上一页</a>
{% endif %}
<span class="current">
第 {{ articles.number }} 页,共 {{ articles.paginator.num_pages }} 页
</span>
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}">下一页</a>
{% endif %}
</div>
{% endif %}
{% endblock %}
URL标签和反转
url标签
url标签允许你引用命名URL配置,而不是硬编码URL。
<!-- 使用命名URL -->
<a href="{% url 'article_detail' article.id %}">查看详情</a>
<!-- 带参数的URL -->
<a href="{% url 'articles_by_category' category.slug %}">{{ category.name }}</a>
<!-- 带多个参数的URL -->
<a href="{% url 'article_edit' article.id user.id %}">编辑</a>
<!-- 使用命名参数 -->
<a href="{% url 'search' q=query page=1 %}">搜索</a>
static标签
static标签用于引用静态文件,如CSS、JavaScript和图像。
{% load static %}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
<link rel="icon" href="{% static 'images/favicon.ico' %}">
</head>
<body>
<img src="{% static 'images/logo.png' %}" alt="Logo">
<script src="{% static 'js/main.js' %}"></script>
</body>
</html>
常用模板标签
include标签
include标签允许你在模板中包含其他模板,这对于重用页面组件非常有用。
<!-- 包含其他模板 -->
{% include 'partials/header.html' %}
{% include 'partials/sidebar.html' %}
<!-- 带上下文的包含 -->
{% include 'partials/comment_form.html' with comment=article.comment %}
<!-- 有条件地包含 -->
{% include 'partials/ad_banner.html' ignore missing %}
with标签
with标签允许你在模板中创建临时变量,对于复杂表达式特别有用。
<!-- 创建临时变量 -->
{% with total=article.views|add:article.likes %}
<p>互动总数:{{ total }}</p>
{% endwith %}
<!-- 复杂表达式 -->
{% with user.profile as profile %}
<p>昵称:{{ profile.nickname }}</p>
<p>积分:{{ profile.points }}</p>
{% endwith %}
cycle标签
cycle标签用于在循环中轮流使用一系列值,常见用途是为表格行交替设置颜色。
<!-- 循环使用值 -->
{% for item in items %}
<tr class="{% cycle 'row1' 'row2' %}">
<td>{{ item.name }}</td>
<td>{{ item.value }}</td>
</tr>
{% endfor %}
自定义模板过滤器
当内置过滤器不能满足需求时,你可以创建自定义过滤器。
创建自定义过滤器
# templatetags/custom_filters.py
from django import template
from django.utils.safestring import mark_safe
import markdown
register = template.Library()
@register.filter
def markdown_to_html(text):
"""将Markdown转换为HTML"""
return mark_safe(markdown.markdown(text))
@register.filter
def pluralize_cn(count, singular, plural):
"""中文复数形式"""
if count == 1:
return singular
else:
return plural
@register.filter
def truncatechars_middle(text, length):
"""从中间截断文本"""
if len(text) <= length:
return text
else:
start = length // 2
end = length - start - 3
return text[:start] + "..." + text[-end:]
在模板中使用自定义过滤器
{% load custom_filters %}
<!-- 使用自定义过滤器 -->
<div class="content">
{{ article.content|markdown_to_html }}
</div>
<p>有 {{ comments.count }} {{ comments.count|pluralize_cn:"条评论":"条评论" }}</p>
<p>{{ long_text|truncatechars_middle:50 }}</p>
自定义模板标签
除了过滤器,你还可以创建自定义标签来执行更复杂的操作。
简单标签
简单标签接收参数并返回一个字符串。
# templatetags/custom_tags.py
from django import template
from django.utils import timezone
from myapp.models import Article
register = template.Library()
@register.simple_tag
def current_time(format_string):
"""显示当前时间"""
return timezone.now().strftime(format_string)
@register.simple_tag
def get_recent_articles(count=5):
"""获取最新文章"""
return Article.objects.filter(
is_published=True
).order_by('-pub_date')[:count]
@register.simple_tag(takes_context=True)
def get_user_articles(context, user):
"""获取用户的文章"""
request = context['request']
return user.article_set.filter(author=request.user)
包含标签
包含标签渲染另一个模板,类似于include标签,但可以包含更多逻辑。
@register.inclusion_tag('tags/article_list.html')
def show_articles(articles, show_author=True):
"""显示文章列表"""
return {
'articles': articles,
'show_author': show_author
}
@register.inclusion_tag('tags/pagination.html', takes_context=True)
def show_pagination(context, page_obj):
"""显示分页"""
request = context['request']
return {
'page_obj': page_obj,
'request': request
}
模板中使用自定义标签
{% load custom_tags %}
<!-- 使用简单标签 -->
<p>当前时间:{% current_time "%Y-%m-%d %H:%M:%S" %}</p>
<!-- 获取最新文章 -->
{% get_recent_articles 3 as recent_articles %}
<ul>
{% for article in recent_articles %}
<li><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></li>
{% endfor %}
</ul>
模板配置
在Django的settings.py中,你可以配置模板引擎的行为。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / 'templates', # 模板目录
],
'APP_DIRS': True, # 自动查找应用中的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',
],
},
},
]
模板最佳实践
模板组织原则
- 使用模板继承减少重复代码
- 将公共部分提取为独立模板
- 合理使用include标签
- 避免在模板中放置复杂逻辑
性能优化
<!-- 使用select_related和prefetch_related优化查询 -->
{% for article in articles.all %}
<!-- 如果需要访问外键对象 -->
<p>作者:{{ article.author.username }}</p>
{% endfor %}
# 在视图中优化查询
def article_list(request):
articles = Article.objects.select_related('author').prefetch_related('tags')
return render(request, 'articles/list.html', {'articles': articles})
安全考虑
- Django模板系统默认对输出进行HTML转义
- 使用
safe过滤器时要谨慎
- 避免在模板中执行危险操作
<!-- 安全的输出 -->
<p>{{ user_input|escape }}</p>
<!-- 只有在信任内容时才使用safe -->
<div>{{ trusted_html|safe }}</div>
课程总结
本节课我们深入学习了Django的模板系统,包括模板语法、标签使用、模板继承、自定义过滤器和标签等内容。模板系统是Django的重要组成部分,掌握它对于构建动态网页至关重要。
通过合理运用模板继承、自定义标签和过滤器,你可以创建出结构清晰、易于维护的模板,同时保持代码的DRY原则。在实际开发中,记得遵循最佳实践,注意性能优化和安全考虑,这将帮助你构建更高效、更安全的Web应用。