#Django Template模板层详解
#课程目标
- 理解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循环 -->
<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: 是否是最后一个元素 -->#模板继承
<!-- 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 -->
<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标签
{% 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 '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 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标签
<!-- 循环使用值 -->
{% 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)#包含标签
@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>#模板配置
#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的重要组成部分,掌握它对于构建动态网页至关重要。

