Flask框架之模板
Jinja2模板引擎简介
模板
在前面的实例中,视图函数的主要作用是生成请求的响应,这是最简单请求.实际上,视图函数有两个作用:
- 处理业务逻辑
- 返回响应内容
在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本.
- 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
- 使用真实值替换变量,再返回最终得到的字符串,这个过程称为'渲染'
- Flask是使用Jinja2这个模板引擎来渲染模板
使用模板的好处
- 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
- 而模板则取到视图函数的数据结果进行展示(试图展示方面)
- 代码结构清晰,耦合度低
Jinja2
两个概念
- Jinja2:是Python下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言
- 模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名
渲染模板函数
- Flask提供的render_template函数封装了该模板引擎
- render_template函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值
使用
- {{}}来表示变量名,这种{{}}语法叫做变量代码块
<h1>{{post.title}}</h1>
Jinja2模板中的变量代码块可以使任意Python类型或者对象,只要它能够被Python的str()方法转化为一个字符串就可以,比如,可以通过下面的方式显示一个字典或者列表中的某个元素
{{your_dict['key']}}
{{your_list[0]}}
- 用{%%}定义的控制代码块,可以实现一些语言层次的功能,比如循环语句
{% if user %}
{{ user }}
{% else %}
hello!
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>
注释
- 使用{##}进行注释,注释的内容不会在html中被渲染出来
{#{{ name }}#}
模板基本使用
- 在项目下创建templates文件夹,用于存放所有模板文件,并在目录下创建一个模板文件html文件temp_demo1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我的模板html内容
</body>
</html>
- 设置templates文件夹属性以便能够在代码中有智能提示
- 设置html中的模板语言,以便在html有智能提示
- 创建视图函数,将该模板内容进行渲染返回
@app.route('/')
def index():
return render_template('temp_demo1.html')
访问: htpp://127.0.0.1:5000/运行测试
模板变量
- 代码中传入字符串,列表,字典到模板中
@app.route('/')
def index():
# 往模板中传入的数据
my_str = 'Hello Word'
my_int = 10
my_array = [3, 4, 2, 1, 7, 9]
my_dict = {
'name': 'xiaoming',
'age': 18
}
return render_template('temp_demo1.html',
my_str=my_str,
my_int=my_int,
my_array=my_array,
my_dict=my_dict
)
- 模板中代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我的模板html内容
<br/>{{ my_str }}
<br/>{{ my_int }}
<br/>{{ my_array }}
<br/>{{ my_dict }}
</body>
</html>
-
运行效果
我的模板html内容
Hello 黑马程序员
10
[3, 4, 2, 1, 7, 9]
{'name': 'xiaoming', 'age': 18} -
相关运算,取值
<br/> my_int + 10 的和为:{{ my_int + 10 }}
<br/> my_int + my_array第0个值的和为:{{ my_int + my_array[0] }}
<br/> my_array 第0个值为:{{ my_array[0] }}
<br/> my_array 第1个值为:{{ my_array.1 }}
<br/> my_dict 中 name 的值为:{{ my_dict['name'] }}
<br/> my_dict 中 age 的值为:{{ my_dict.age }}
- 结果
my_int + 10 的和为:20
my_int + my_array第0个值的和为:13
my_array 第0个值为:3
my_array 第1个值为:4
my_dict 中 name 的值为:xiaoming
my_dict 中 age 的值为:18
示例代码:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
my_int = 18
my_str = 'curry'
my_list = [1, 5, 4, 3, 2]
my_dict = {
'name': 'durant',
'age': 28
}
# render_template方法:渲染模板
# 参数1: 模板名称 参数n: 传到模板里的数据
return render_template('01_template.html',
my_int=my_int,
my_str=my_str,
my_list=my_list,
my_dict=my_dict)
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>我是模板</h2>
{{ my_int }}
<br>
{{ my_str }}
<br>
{{ my_list }}
<br>
{{ my_dict }}
<hr>
<h2>模板的list数据获取</h2>
<hr>
{{ my_list[0] }}
<br>
{{ my_list.1 }}
<hr>
<h2>字典数据获取</h2>
<hr>
{{ my_dict['name'] }}
<br>
{{ my_dict.age }}
<hr>
<h2>算术运算</h2>
<br>
{{ my_list.0 + 10 }}
<br>
{{ my_list[0] + my_list.1 }}
</body>
</html>
过滤器
过滤器本质就是函数。
有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用Python中的某些方法,那么这就用到了过滤器
使用方法:
- 过滤器的使用方法为:变量名 | 过滤器.
- {{ variable | filter_name(*args)}}
- 如果没有任何参数传给过滤器,则可以把括号省略掉
- {{variable | filter_name}}
- 如:'',这个过滤器的作用:把变量variable的值首字母首字母转化为大写,其余字母转化为小写
链式调用
在Jinja2中,过滤器支持链式调用,示例如下:
{{'hello word' | reverse | upper}}
输出结果为:
DROW OLLEH
常见内建过滤器
- safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
- capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>
- lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>
- upper:把值转成大写
<p>{{ 'hello' | upper }}</p>
- title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>
- reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>
- format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>
- striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>
- truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>
列表操作
- first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
- last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
- length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
- sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
- sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
语句块过滤
{% filter upper %}
#一大堆文字#
{% endfilter %}
自定义过滤器
过滤器本质是函数,当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种方式实现。
- 一种是通过Flask应用对象的add_template_filter方法
- 通过装饰器来实现自定义过滤器
重要:自定义过滤器名称如果和内置过滤器重名,会覆盖内置的过滤器
需求:添加列表反转的过滤器
方式一
通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:
def do_listreverse(li):
# 通过原列表创建一个新列表
temp_li = list(li)
# 将新列表进行返转
temp_li.reverse()
return temp_li
app.add_template_filter(do_listreverse,'lireverse')
方式二
用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('lireverse')
def do_listreverse(li):
# 通过原列表创建一个新列表
temp_li = list(li)
# 将新列表进行返转
temp_li.reverse()
return temp_li
- 在html中使用该自定义过滤器
<br/> my_array 原内容:{{ my_array }}
<br/> my_array 反转:{{ my_array | lireverse }}
- 运行结果
my_array 原内容:[3, 4, 2, 1, 7, 9]
my_array 反转:[9, 7, 1, 2, 4, 3]
完整代码:
from flask import Flask, render_template
app = Flask(__name__)
# 1.自定义列表反转函数
@app.template_filter('list_reverse')
# 方法二:使用装饰器
def list_reverse(list):
# list = []
list.reverse()
return list
# 2.将自定义的函数添加到flask过滤器中
app.add_template_filter(list_reverse, 'list_reverse')
@app.route('/')
def index():
list = [1, 3, 4, 5, 2] # 2,5,4,3,1
return render_template('03_diy.html', list=list)
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>自定义过滤器</h2>
<br>
{{ list }}
<br>
{{ list | list_reverse }}
</body>
</html>
控制代码块
控制代码块主要包含两个:
if/else if/else / endif
for / endfor
if语句
Jinja2语法中的if语句跟Python中的if语句相似,后面的布尔值或者返回布尔值的表达式将决定代码中那个流程会被执行:
{% if item.id != 5 %}
{% if loop.index == 1 %}
<li style="background-color: deeppink">{{ item.value }}</li>
{% endif %}
{% endif %}
循环
- 我们可以在Jinja2中使用循环来迭代任何列表或者生成器函数
{% for post in posts %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
- 循环和if语句可以组合使用
{% for post in posts if post.text %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
- 在一个for循环块中你可以访问这些特殊的变量
变量 | 描述 |
---|---|
loop.inde | 当前循环迭代的次数(从1开始) |
loop.index0 | 当前循环迭代的次数(从0开始) |
loop.revindex | 到循环结束需要迭代的次数(从1开始) |
loop.revindex0 | 到循环结束需要迭代的次数(从0开始) |
loop.frist | 如果是第一次迭代,为True |
loop.last | 如果是最后一次迭代,为True |
loop.length | 序列中的项目数 |
loop.cycle | 在一串序列间期取值的辅助函数 |
实例程序
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
# 只显示4行代码,颜色为黄绿红紫
my_list = [
{
"id": 1,
"value": "我爱工作"
},
{
"id": 2,
"value": "工作使人快乐"
},
{
"id": 3,
"value": "沉迷于工作无法自拔"
},
{
"id": 4,
"value": "日渐消瘦"
},
{
"id": 5,
"value": "以梦为马,越骑越傻"
}
]
return render_template('04_hold.html', my_list=my_list)
if __name__ == '__main__':
app.run(debug=True)
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
控制代码块使用
<br>
<hr>
{% for item in my_list %}
{% if item.id != 5 %}
{% if loop.index == 1 %}
<li style="background-color: deeppink">{{ item.value }}</li>
{% elif loop.index == 2 %}
<li style="background-color: cyan">{{ item.value }}</li>
{% elif loop.index == 3 %}
<li style="background-color: darkorchid">{{ item.value }}</li>
{% elif loop.index == 4 %}
<li style="background-color: yellow">{{ item.value }}</li>
{% endif %}
{% endif %}
{% endfor %}
</body>
</html>
模板继承
在模板中,可能会遇到以下情况:
- 多个模板具有完全相同的顶部和底部内容
- 多个模板中具有相同的模板代码内容,但是内容中部分值不同
- 多个模板中具有完全相同的html代码块内容
像遇到这种情况,可以使用Jinja2模板中的继承来进行实现
模板继承是为了重用模板中的公共内容.一般Web开发中,继承主要使用在网站的顶部菜单,底部.这些内容可以定义在父模板中,子模板直接继承,不需要重写
- 定义标签的内容
{% block top %} {% endblock%}
- 相当于在父模板中挖个坑,当子模板继承父模板时候,可以进行填充
- 子模板使用extends指令声明这个模板继承自哪个模板
- 父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()
示例代码:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/parent')
def parent():
return render_template('06_one.html')
@app.route('/child')
def child():
return render_template('07_tow.html')
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>我是头部</h2>
<hr>
{# 将父模板中需要重写的内容使用block包含起来 #}
{% block content %}
<h2>我是父模板内容</h2>
{% endblock %}
<hr>
<h2>我是底部</h2>
</body>
</html>
{# 1 使用extends关键字集成模板 #}
{% extends '06_one.html' %}
{# 重写block #}
{% block content %}
<h2>我是子模板内容</h2>
{% endblock %}
特有变量和函数
你可以在自己的模板中访问一些 Flask 默认内置的函数和对象
config
你可以从模板中直接访问Flask当前的config对象
{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db
request
就是Flask中代表当前请求的request对象
{{request.url}}
http://127.0.0.1
session
为Flask的session对象
{{session.new}}
True
g变量
在视图函数中设置g变量的name属性的值,然后在模板中直接可以取出
{{ g.name }}
url_for()
url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接:
{{url_for('home')}}
/
如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中:
{{ url_for('post', post_id=1)}}
/post/1