1 简介
1.1 什么是模板
视图函数的作用是生成请求的响应,这种响应通常就是用户所看到的html网页。模板就是带有模板语法({{ }}, {% %}
)的html文件,它能接收视图函数传递的变量,通过模板引擎,模板被渲染成html页面。
1.2 语法
在模板中,使用{{ }}
结构表示一个变量,它是一种特殊的占位符,告诉模板引擎,这个位置的值从渲染模板时传入的变量获取;使用{% %}
结构渲染标签(控制结构)。
1.3模板查找机制
模板一般放在应用下的templates文件夹,Django的模板渲染引擎会自动查找所有应用下这个文件夹,直到找到指定的模板,因此如果不同应用下有同名的模板,Django很可能找错。因此建议的做法是,在每个应用下的templates文件夹下创建以应用名命名的文件夹,将与该应用相关的模板放在这个文件夹中,就像这样:app/templates/app/XXX.html
。当视图函数返回render响应时,相应地在模板名称前加上app的名字,就像这样:return render('app/XXX.html')
2 模板变量
2.1 深度查询
Django的模板引擎能识别python中所有类型的变量,对于列表、字典、和对象这些复杂的变量,我们可以通过万能的句点法(深度查询)来访问集合或对象,比如:
<p>对列表取值:{{ list.index }}</p>
<P>对字典取值: {{ dict.key }}</P>
<P>访问对象的属性: {{ obj.attr }}</P>
2.2 使用变量过滤器
作用:修改变量在模板中的显示
格式:{{ var|filter: arg }}
注意,过滤器函数默认接收变量,除此之外,最多只能接收一个参数
看个栗子:
1.我们在视图函数中定义一些变量,通过locals()
全部传入模板渲染
def index(request):
string = 'he l lo'
day = datetime.datetime.now()
l = []
post = "this is my post, it's very very very long"
k = ['cat', 'dog', 'pig']
size = 12345678
return render(request, 'blog/index.html', locals()) # 注意,blog前不要加/
# locals() 将视图中的所有变量对应到模板中的变量
2.在模板中应用过滤器来修改变量
<h1>filter过滤器效果!</h1>
<h3>{{ string }} --> {{ string|cut:" " }} 剔除指定字符串</h3>
<h3>{{ day }} --> {{ day|date:"Y-m-d" }} 格式化日期</h3>
<h3>{{ l }} --> {{ l|default:"查询结果为空" }} 当集合为空时,设置默认显示,比如数据库查询结果为空时,我们自定义前端的提示信息</h3>
<h3>{{ post }} --> {{ post|truncatewords:6 }} 截断指定数量的单词,可用于显示文章摘要</h3>
<h3>{{ k }} --> {{ k|join:", " }} 拼接列表</h3>
<h3>{{ size }} --> {{ size|filesizeformat }} 显示友好的文件大小</h3>
3.浏览器显示效果:
4.变量过滤器一览
过滤器 | 说明 |
---|---|
add | 给变量加上一个值 |
addslashes | 给变量中的引号前加/斜线 |
capfirst | 首字母大写 |
lower | 转换为小写 |
truncatewords:30 | 截断指定数量的单词,可用于显示文章摘要 |
join | 用指定字符拼接列表 |
default | 如果变量为空或false,使用默认值 |
length | 显示字符串/列表的长度 |
filesizeformat | 显示易读的文件大小 |
date | 格式化日期字符串 |
cut | 从字符串中移除指定的字符 |
safe | 渲染值时不转义。出于安全考虑,模板引擎会转义所有的变量。如果确实需要显示变量中的html代码,就可以使用safe过滤器。不要对不可信的变量使用safe过滤器,比如用户提交的表单中输入的文本,可能包含攻击代码。 |
过滤器也支持链式操作:
{{ var|filter1|filter2 }}
5.更多过滤器的使用,请参考 built-in filter reference
2.3 自定义过滤器和标签
2.3.1 自定义过滤器
过滤器本质就是一个函数,只需要如下几步,我们也可以定制自己的过滤器
1. 将当前应用添加到settings.py的INSTALLED_APPS
列表中
2. 在项目的应用文件夹下新建一个包,命名为templatetags
3. 在刚刚新建的包下写一个脚本,比如my_filter.py:
```
from django import template
register = template.Library()
# 导入template 并实例化一个对象,命名为 register
@register.filter # 装饰自定义函数为过滤器
def num_to_char(x): # 自定义函数,将数字转为字符
return chr(x)
```
4. 在模板的<head>
标签内中导入自定义的过滤器(使用前导入就行,不一定非要在<head>
内):{% load my_filter %}
。另外,发现一个问题,自定义过滤器导入后,自带的过滤器可能工作不正常,具体原因不明,有知道的伙伴可以分享下。
5. 重启服务,你就可以使用自定义的过滤器了
2.3.2 自定义标签
自定义标签的流程和自定义过滤器是相同的,只是装饰器修改为@register.simple_tag
另外在使用格式上,自定义标签是这样:
{% func var [arg1] [arg2] ... [argn] %}
二者的其他差异:
1. 过滤器除了接收变量本身,最多可以额外接收一个参数,而自定义标签无限制
2. 过滤器可以配合其它标签使用,比如if控制语句:
```
{% if var|filter %}
do something
```
但是自定义标签不能这样使用。
补充:自定义标签的另一种形式 inclusion_tag:
应用下新建
templatetags
目录,新建脚本custom_tag.py
,通过inclusion_tag
自定义标签from django.template.library import Library register = Library() @register.inclusion_tag('sample.html') def get_result(x): print('do something') return {'content': 'XXX', 'comments': ['aa', 'bb', 'cc']}
说明:
inclusion_tag
接收一个模板路径,这里是”sample.html”,并将函数的返回值拿到模板中渲染- “sample.html”模板的渲染结果,返回给调用该自定义标签的模板中,即步骤3的模板。
定义”sample.html”模板:
<div id="body"> {{ content }} </div> <ul id="comment_list"> {% for comment in comments %} <li>{{ comment }}</li> {% endfor %} </ul>
调用自定义标签:
{% load custom_tag %} <div id="article"> {% get_result x %} </div>
3 控制结构
Django的模板引擎提供了多种控制结构,用来改变模板的渲染流程。
3.1 if语句
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, {{ Stranger }}!
{% endif %}
3.2 for循环
渲染从数据库取出的一组评论,使用for循环可以实现这一需求
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
3.2.1 forloop变量
forloop用在for循环内,配合if语句,可以用来控制循环:
1. forloop.counter 索引,从1开始,可用于显示清单中条目的序号
2. forloop.counter0 索引,从0开始
3. forloop.revcounter 索引,倒序到1
4. forloop.revcounter0 索引,倒序到0
5. forloop.first 当第一次循环时,值为True
6. forloop.last 当第一次循环时,值为False
看个栗子:
<ul>
{% for comment in comments %}
<!-- 如果是第一条评论,就给它加某种样式 -->
{% if forloop.first %}
<li class="first">
{% else %}
<li>
{% endif %}
{{ comment }}
</li>
{% endfor %}
</ul>
3.2.2 for … empty
for循环可以可以接受一个{% empty %}
从句,如果循环的对象为空,将显示{% empty %}
从句下的内容:
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% empty %}
<li>No comments yet!</li>
{% endfor %}
</ul>
3.3 代码重用
3.3.1 模板继承
模板继承(extends)类似Python中的类继承。它允许我们重用模板中相同的部分,定制不同的部分。首先,创建一个名为base.html的基模板:
<html>
<head>
{% block head %}
<title>{% block title %}Title{% endblock %}</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
block标签定义的块可以在衍生模板中修改,并且每个block标签都有名字。这里,我们只是简单定义了head, title(因为每个页面都应该显示一个不同title), body的块。注意,title包含中head中。下面我们来写一个基模板的衍生模板:
{% extends 'base.html' %}
{% block title %}Home{% endblock %}
{% block head %}
{{ block.super }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}}
{% extends 'base.html' %}
继承指令放在首行,声明该模板衍生自base.html。我们在衍生模板中重定义了三个block的内容,模板引擎会根据block名字,将其插入适当的位置。另外,通过{% block.super %}
,可以重用原来block中的内容,以便在其基础上添加内容。
3.3.2 模板包含
需要在多处重复使用的模板代码片段可以写在单独的文件,再包含(include)在要用的模板中,以避免重复:
{% include 'common.html' %}
3.4 其它标签
{% url %}
通过视图函数别名,生成url绝对路径,它还可以接收路由分组的参数:{% url 'name' v1 v2 %}
{% with %}
用更简单的变量名替代复杂的变量名
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
<!-- 这里变量business.employees.count赋值给total, 方便后续使用;当然也可以采用下面的写法 -->
{% with business.employees.count as total %}
...
{% endwith %}
{% verbatim %}
告诉模板引擎,不要渲染这个标签内的内容
{% verbatim %}
{{ 我就是要显示两个花括号! }}
{% endverbatim %}
{% load %}
载入自定义的模板标签,记住,目标不加引号
更多标签,请参考官网built-in-tag-reference
模板上下文处理器
令某些变量在所有模板中可用,比如,网站的顶部分类菜单,在很多页面中都是相同的。
settings.py:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
# ...
'app.views.get_funcs', # 视图加入上下文处理器列表
],
},
},
]
FUNCTIONS = [
(1,"园子"),
(2,"随笔"),
(3,"新闻"),
(4,"博文"),
]
views.py
def get_funcs(request):
return ({"funcs": settings.FUNCTIONS})
template: funcs变量在所有模板中可用
{% for func in funcs %}
<li><a href="#">{{ func.1 }}</a></li>
{% endfor %}