8、数据渲染与显示
1 概述
- Django作为Web框架,需要一种很便利的方法动态地生成HTML网页,因此有了模板这个概念。模板包含所需HTML的部分代码以及一些特殊语法,特殊语法用于描述如何将视图传递的数据动态插入HTML网页中。
- Django可以配置一个或多个模板引擎(甚至是0个,如前后端分离,Django只提供API,无须使用模板引擎),模板引擎有 Django 模板语言(Django Template Language,DTL)和Jinja2。Django 模板语言是Django内置的功能之一,它包含模板上下文(也可称为模板变量)标签和过滤器,各个功能说明如下:
- 模板上下文是以变量的形式写入模板文件中的,变量值由视图函数或视图类传递所得。
- 标签是对模板上下文进行控制输出,比如模板上下文的判断和循环控制等。
- 模板继承隶属于标签,它是将每个模板文件重复的代码抽取出来并写在一个共用的模板文件中,其他模板文件通过继承共用模板文件来实现完整的网页输出。
- 过滤器是对模板上下文进行操作处理,比如模板上下文的内容截取、替换或格式转换等。
2 模板上下文
-
模板上下文(也可以称为模板变量)是模板中基本的组成单位,上下文的数据由视图函数或视图类传递。它以 { { variable }} 表示,variable是上下文的名称,它支持Python所有的数据类型,如字典、列表、元组、字符串、整型或实例化对象等。上下文的数据格式不同,在模板中的使用方式也有所差异,示例代码如下:
#假如 variablel = '字符串或整型' <div>{{ variablel}}</div> # 输出 "<div>字符串或整型</div>"# 假如 variable2 = {'name' : '字典或实例化对象'} <div>{{ variable2.name }}</div> #输出 "<div>字典或实例化对象</div>"#假如 variable3 = ['元组或列表'] <div>{{ variable3.0 }}</div> #输出 "<div>元组或列表</div>"
-
从上述代码发现,如果上下文的数据带有属性,就可以在上下文的末端使用
“.”
来获取某个属性的值。比如上下文为字典或实例化对象,在上下文末端使用“.”
并写入属性名称即可在网页上显示该属性的值;若上下文为元组或列表,则在上下文末端使用 “”并设置索引下标来获取元组或列表的某个元素值。 -
如果视图没有为模板上下文传
”
递数据或者模板上下文的某个属性、索引下标不存在,Django 就会将其设为空值。例如获取 variable2的属性age,由于上述的variable2并不存在属性age,因此网页上将会显示 “ -
在PyCharm的Debug调试模式下分析Django模板引擎的运行过程。打开函数render所在的源码文件,变量content是模板文件的解析结果,它是由函数render_to_string完成解析过程的如图6-6所示。
def render(request, template_name, context=None, content_type=None, status=None, using=None):"""Return an HttpResponse whose content is filled with the result of callingdjango.template.loader.render_to_string() with the passed arguments."""content = loader.render_to_string(template_name, context, request, using=using)return HttpResponse(content, content_type, status)
-
想要分析Diango模板引擎的解析过程,还需要从函数 render_to_string 深入分析,通过PyCharm打开函数 render_to_string 的源码信息,发现它调用了函数 get_template 或 select_template,模板的解析过程:
3 内置标签
-
标签是对模板上下文进行控制输出,它是以 {% tag %} 表示的,其中tag是标签的名称,Django内置了许多模板标签,比如{% if %}(判断标签)、{% for %}(循环标签)或{% url %)(路由标签)等。
-
内置的模板标签可以在Django源码(\django\template\defaulttags.py)中找到定义过程,每个内置标签都有功能注释和使用方法,本书只列举常用的内置标签:
标签名称 语法 功能描述 示例 if
{% if condition %} ... {% elif condition %} ... {% else %} ... {% endif %}
条件判断,支持 and
,or
,not
,==
,!=
,>
,<
,>=
,<=
等运算符{% if user.is_staff %} Admin Panel {% else %} User Dashboard {% endif %}
for
{% for item in list %} ... {% empty %} ... {% endfor %}
迭代列表、查询集等可迭代对象,支持 forloop.counter
,forloop.revcounter
等变量{% for comment in post.comments %} { { comment.text }} {% empty %} No comments {% endfor %}
csrf_token
{% csrf_token %}
生成 CSRF 保护令牌,用于 POST 请求表单防跨站攻击 <form method="post">{% csrf_token %} <button>Submit</button> </form>
url
{% url 'view_name' arg1 arg2 %} 或 {% url 'app_name:view_name' %}
反向解析 URL 路径,避免硬编码 URL {% url 'blog:article_detail' article.slug %}
load
{% load static i18n %}
加载自定义或内置模板标签库(如 static
,humanize
,i18n
等){% load static %}
或{% load custom_tags %}
static
{% static 'path/to/file.css' %}
生成静态文件的 URL 路径(需先 {% load static %}
)<link href="{% static 'css/style.css' %}" rel="stylesheet">
extends
{% extends "base.html" %}
声明模板继承关系,必须放在模板文件顶部 {% extends "layouts/main.html" %}
block
{% block block_name %} ... {% endblock %}
定义可被子模板覆盖的内容区块,常用于模板继承 {% block content %} Main content here {% endblock %}
- 关键说明:
- 模板继承顺序:
extends
标签必须位于模板文件的第一行 - CSRF 保护:所有 POST 表单必须包含
csrf_token
标签 - 静态文件路径:
static
标签会自动添加STATIC_URL
设置前缀 - URL 命名空间:推荐使用
app_name:view_name
格式避免命名冲突 - 循环变量:
for
标签内置forloop
对象包含counter
,counter0
,first
,last
等属性
- 模板继承顺序:
- 关键说明:
-
for 标签模板变量
变量名称 描述 示例用法 forloop.counter
当前循环的迭代次数(从 1 开始) { { forloop.counter }}
输出:1, 2, 3…forloop.counter0
当前循环的迭代次数(从 0 开始) { { forloop.counter0 }}
输出:0, 1, 2…forloop.revcounter
剩余迭代次数(从总数开始递减) 若总迭代 3 次,输出:3, 2, 1 forloop.revcounter0
剩余迭代次数(从总数 - 1 开始递减) 若总迭代 3 次,输出:2, 1, 0 forloop.first
当前是否为第一次迭代(布尔值) {% if forloop.first %} First item {% endif %}
forloop.last
当前是否为最后一次迭代(布尔值) {% if forloop.last %} Last item {% endif %}
forloop.parentloop
嵌套循环中引用父级循环的 forloop
对象内层循环中使用 { { forloop.parentloop.counter }}
4 模板继承
-
模板继承是通过模板标签来实现的,其作用是将多个模板文件的共同代码集中在一个新的模板文件中,然后各个模板可以直接调用新的模板文件,从而生成HTML网页,这样可以减少模板之间重复的代码。
{% extends 'base.html' %} {% block title %} <title>首页</title> {% endblock %}{% block body %}<a href="{% url 'index:index' %}">首页</a><h1>Hello,Django</h1> {% endblock %}
-
模板的继承与Python的类继承原理是一致的,通过继承方式使得其具有父类的功能和属性,同时也可以通过重写来实现更加复杂的开发需求。
5 内置过滤器
-
过滤器主要是对上下文的内容进行操作,如:替换、反序和转义等。通过过滤器处理上下文可以将其数据格式或内容转化为我们想要的显示效果,而且可以相应地减少视图的代码量。过滤器的使用方法如下:
- { { variable | filter }}
-
若上下文设有过滤器,则模板引擎在解析上下文时,首先由过滤器 filter 处理上下文variable,然后对处理后的结果进行解析并显示在网页上。variable代表模板上下文,管道符号 “|” 代表当前上下文使用过滤器,filter代表某个过滤器。单个上下文可以支持多个过滤器同时使用,例如:
- { { variable | filter | lower})
-
在使用的过程中,有些过滤器还可以传入参数,但仅支持传入一个参数。带参数的过滤器与参数之间使用冒号隔开,并且两者之间不能留有空格,例如:
- { { variable | date:“D d M Y”}}
-
Django的内置过滤器可以在源码(\django\template\defaultfilters.py) 中找到具体的定义过程:
过滤器名称 语法 功能描述 示例 length
`value length` 返回字符串或列表的长度 default
`value default:“N/A”` 若值为假(空字符串、None 等),返回默认值 date
`date_obj date:“Y-m-d”` 格式化日期时间对象,支持 Y
,m
,d
,H
,i
,s
,N
等格式代码upper
/lower
`text upper` 将字符串转换为大写 / 小写 truncatewords
`text truncatewords:3` 截断字符串为指定单词数,超出部分用省略号表示 safe
`html safe` 标记字符串为安全 HTML,避免自动转义 add
`num add:5` 数值相加(或字符串拼接) join
`list join:", "` 类似 Python 的 join()
方法,将列表元素连接为字符串filesizeformat
`bytes filesizeformat` 将字节数格式化为人类可读的文件大小(KB、MB 等) slice
`list slice:“:2”` 切片操作,类似 Python 的 [:2]
linebreaks
`text linebreaks` 将换行符转换为 HTML 的 <br>
或<p>
标签yesno
`bool yesno:“Y,N,?”` 将布尔值转换为自定义文本(是 / 否 / 未知) divisibleby
`num divisibleby:2` 判断数值是否能被整除(返回布尔值) floatformat
`num floatformat:2` 格式化浮点数,指定小数位数(四舍五入) urlencode
`text urlencode` 将字符串编码为 URL 安全格式 -
补充说明:
- 链式过滤器:可组合使用多个过滤器,如
{ { value|lower|truncatewords:5 }}
- 安全注意:使用
safe
过滤器时需确保内容来源安全,避免 XSS 攻击 - 自定义过滤器:可通过
{% load %}
标签加载自定义过滤器库
- 链式过滤器:可组合使用多个过滤器,如
-
注意
- 使用过滤器过程中,上下文、管道符号 “|” 和 过滤器之间没有规定使用空格隔开。为了符合编码的规范性,建议使用空格隔开。
- 若过滤器需要设置参数,过滤器、冒号和参数之间不能有供果,否则会提示:TemplateSyntaxError
6 自定义异常页面
-
网站异常是一个普遍现象,常见的异常以 404 和 500 为主,出现异常的网站自身数据缺陷或不合理的非法访问导致,
-
实现自定义异常页面,操作如下:
-
在你的项目模板目录下(通常是
templates/
)创建以下两个模板文件:-
404.html(处理未找到页面)
-
500.html(处理服务器内部错误)
<!-- templates/404.html --> <!DOCTYPE html> <html> <head><title>404 - 页面未找到</title> </head> <body><h1>页面不存在</h1><p>你请求的页面不存在,请检查URL或返回<a href="/">首页</a>。</p> </body> </html><!-- templates/500.html --> <!DOCTYPE html> <html> <head><title>500 - 服务器错误</title> </head> <body><h1>服务器开小差了</h1><p>我们正在修复问题,请稍后再试。</p> </body> </html>
-
-
配置 URL 处理函数(可选)
-
Django 默认会自动处理 404 和 500 错误,但如果你需要自定义处理逻辑,可以在项目的
urls.py
中添加:# project/urls.py from django.conf.urls import handler404, handler500 from django.contrib import admin from django.urls import pathurlpatterns = [path('admin/', admin.site.urls),# 其他URL配置... ]# 指定错误处理视图 handler404 = 'your_app.views.custom_404' handler500 = 'your_app.views.custom_500'
-
-
创建自定义视图函数(可选)
-
如果你在第 2 步中指定了自定义处理函数,需要在对应应用的
views.py
中实现:# your_app/views.py from django.shortcuts import renderdef custom_404(request, exception):return render(request, '404.html', status=404)def custom_500(request):return render(request, '500.html', status=500)
-
-
-
注意
- DEBUG 模式:仅在
DEBUG=False
时才会显示自定义错误页面 - 500 错误处理:
handler500
不接收exception
参数,因为服务器错误可能无法提供具体异常信息 - 静态文件:确保错误页面不依赖动态数据,避免在出错时引发更多问题
- DEBUG 模式:仅在
7 基础模板设计
-
从网页静态界面看到,所有网页(除了登录页外)的顶部都是相同的,包含商品搜索功能和网站导航
-
由于每个网页的顶部都相同,也就是说这部分的HTML代码是完全相同的,因此我们可以将这部分代码单独抽取出来并放置在一