文章目录
- Jinja2模板
- 一、Jinja2模板介绍
- 1.模板传参
- 2.语法
- 二、表达式
- 三、控制语句
- 1.条件判断语句
- 2.for循环语句:
- 四、过滤器
- 1.什么是过滤器
- 2.字符串的过滤器
- 3.数值过滤器
- 4.列表相关过滤器
- 5.字典相关过滤器
- 6.自定义过滤器
- 五、测试器
- 1.Jinja2中内置的测试器
- 2.自定义测试器
- 六、块和继承
- 1.Jinja2模板
- 2.include标签
- 七、模板中的url_for函数
- 1、动态超链接
- 2、加载静态文件
Jinja2模板
视图函数的主要作用是生成请求的响应,这是最简单请求。实际上,视图函数有两个作用:
- 处理业务逻辑
- 返回响应内容
在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。
模板是什么:
-
模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
-
使用真实值替换变量,再返回最终得到的字符串,这个过程称为
渲染
-
Flask
是使用Jinja2
这个模板引擎来渲染模板
使用模板的好处:
-
视图函数只负责业务逻辑和数据处理(业务逻辑方面)
-
而模板则取到视图函数的数据结果进行展示(视图展示方面)
-
代码结构清晰,耦合度低
三层代码结构
一、Jinja2模板介绍
Jinja2
:是Python的Web项目中被广泛应用的模板引擎,是由Python
实现的模板语言,Jinja2
的作者也是 Flask
的作者。他的设计思想来源于Django
的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask
内置的模板语言。
jinja2
之所以被广泛使用是因为它具有以下优点:
-
相对于
Template
,jinja2
更加灵活,它提供了控制结构,表达式和继承等。 -
相对于
Mako
,jinja2
仅有控制结构,不允许在模板中编写太多的业务逻辑。 -
相对于
Django
模板,jinja2
性能更好。 -
Jinja2
模板的可读性很棒。
要渲染一个模板,通过 render_template
方法即可。
1.模板传参
1、在使用 render_template
渲染模版的时候,可以传递关键字参数(命名参数),以后直接在模版中使用就可以了。
2、如果你的参数项过多,那么可以将所有的参数放到一个字典中,或者列表中都可以。一般如果想将字典打散成关键字参数可以在参数的前面加 **
from flask import Flask, render_templateapp = Flask(__name__, static_url_path='/flyme', static_folder='static')STUDENT = {'name': 'zhang san', 'age': 22, 'gender': '男'}STUDENT_LIST = [{'name': 'zhang san', 'age': 22, 'gender': '男'},{'name': 'li si', 'age': 22, 'gender': '女'},{'name': 'wang wu', 'age': 22, 'gender': '男'}]STUDENT_DICT = {'a': {'name': 'zhang san', 'age': 22, 'gender': '男'},'b': {'name': 'li si', 'age': 22, 'gender': '女'},'c': {'name': 'wang wu', 'age': 22, 'gender': '男'}}@app.route('/')
def hello_world(): # put application's code herereturn 'Hello World!'@app.route('/1')
def test1():return render_template('student.html', **STUDENT) # 为了方便在模板中使用,可以把字典打散@app.route('/2')
def test2():return render_template('student_list.html', student_list=STUDENT_LIST)@app.route('/3')
def test3():return render_template('student_dict.html', student_dict=STUDENT_DICT)if __name__ == '__main__':app.run(host='0.0.0.0', port=8000, debug=True)
2.语法
在jinja2
中,存在三种语法:
-
控制结构 (逻辑代码):
{% %}
-
变量取值:
{{ }}
-
注释:
{# #}
student.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STUDENT</title>
</head>
<body>
学生的姓名:{{ name }} <br>
学生的年龄:{{ age }} <br>
学生的性别:{{ gender }}
</body>
</html>
访问
二、表达式
-
最常用的是变量,由
Flask
渲染模板时传过来,比如name
-
也可以是任意一种
Python
基础类型,比如字符串{{stu_list}}
;或者数值,列表,元祖,字典,布尔值。 -
运算。包括算数运算,如
{{ 2 + 3 }}
;比较运算,如{{ 2 > 1 }}
;逻辑运算,如{{ False and True }}
-
过滤器|和测试器
is
-
函数调用,如
{{ current_time() }}
; -
数组下标操作,如
{{ arr[1] }}
-
in操作符,如
{{ 1 in [1,2,3] }}
-
字符串连接符
~
,作用同Python中的“+”
一样,如{{ "Hello " ~ name ~ "!" }}
-
None
值处理{{name or ""}
三、控制语句
Jinja2
的控制语句主要就是条件控制语句if
,和循环控制语句for
,语法类似于Python if-else
1.条件判断语句
{% if %}
{% elif %}
{% else %}
{% endif %}
2.for循环语句:
{% for stu in stu_list%}
{{ stu }}
{% endfor %}
例:条件语句
参数列表
STUDENT = {'name': 'zhang san', 'age': 22, 'gender': '男'}STUDENT_LIST = [{'name': 'zhang san', 'age': 22, 'gender': '男'},{'name': 'li si', 'age': 22, 'gender': '女'},{'name': 'wang wu', 'age': 22, 'gender': '男'}]STUDENT_DICT = {'a': {'name': 'zhang san', 'age': 22, 'gender': '男'},'b': {'name': 'li si', 'age': 22, 'gender': '女'},'c': {'name': 'wang wu', 'age': 22, 'gender': '男'}}
student.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STUDENT</title>
</head>
<body>
学生的姓名:{{ name }} <br>
学生的年龄:{{ age }} < >
{% if age >= 18 %}已经成年
{% else %}未成年
{% endif %}<br>
学生的性别:{{ gender }}
</body>
</html>
@app.route('/1')
def test1():return render_template('student.html', **STUDENT) # 为了方便在模板中使用,可以把字典打散
student_list.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STUDENT_LIST</title>
</head>
<body>
{#{{ student_list }}#}
<table border="1px"><tr><td>序号</td><td>姓名</td><td>年龄</td><td>性别</td></tr>{% for stu in student_list %}<tr><td>{{ loop.index }}</td> {# 序号 #}<td>{{ stu.name }}</td>{% if 60 > stu.age >= 18 %}<td>已成年</td>{% elif stu.age >= 60 %}<td>已退休</td>{% else %}<td>未成年</td>{% endif %}<td>{{ stu.gender }}</td></tr>{% endfor %}
</table>
</body>
</html>
@app.route('/2')
def test2():return render_template('student_list.html', student_list=STUDENT_LIST)
循环语句
student_dict.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STUDENT_DICT</title>
</head>
<body>
{#{{ student_list }}#}
<table border="1px"><tr><td>序号</td><td>姓名</td><td>年龄</td><td>性别</td></tr>{% for stu_key, stu in student_dict.items() %}<tr><td>{{ loop.index }},key:{{ stu_key }}</td><td>{{ stu.name }}</td>{% if 60 > stu.age >= 18 %}<td>已成年</td>{% elif stu.age >= 60 %}<td>已退休</td>{% else %}<td>未成年</td>{% endif %}<td>{{ stu.gender }}</td></tr>{% endfor %}
</table>
</body>
</html>
@app.route('/3')
def test3():return render_template('student_dict.html', student_dict=STUDENT_DICT)
四、过滤器
1.什么是过滤器
什么是过滤器? 实质上就是一个转换函数。变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja
2里面的内置函数和字符串处理函数。
常用的过滤器有:
过滤器名称 | 说明 |
---|---|
safe | 渲染时值不转义 |
capitialize | 把值的首字母转换成大写,其他子母转换为小写 |
lower | 把值转换成小写形式 |
upper | 把值转换成大写形式 |
title | 把值中每个单词的首字母都转换成大写 |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所有的HTML标签都删掉 |
join | 拼接多个值为字符串 |
replace | 替换字符串的值 |
round | 默认对数字进行四舍五入,也可以用参数进行控制 |
int | 把值转换成整型 |
2.字符串的过滤器
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>StringFilter</title>
</head>
<body>{# 当变量数据没有传入的时候,可以传入默认值 #}
<p>{{ name | default('默认值') }}</p> <br>{# 字符串大小写转换 #}
<p>{{ 'XYZ' | lower }}</p> <br>{# 翻转字符串 #}
<p>{{ 'bei ji de san ha' | reverse }}</p> <br>{#格式换字符串#}
<p>{{ '今天是%d号,天气%s' | format(5, '晴') }}</p> <br>{# 不需要转义 #}
<p>{{ '<h1>name</h1>' | safe }}</p> <br>
</body>
</html>
@app.route('/string')
def stringFilter():return render_template('string_filter.html')
3.数值过滤器
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>NumberFilter</title>
</head>
<body>
{# 四舍五入,取整数 #}
<p>{{ 3.14159 |round }}</p>{# 小数点后截取2位 #}
<p>{{ 3.14159 |round(2) }}</p>{# 取绝对值 #}
<p>{{ -1001 |abs }}</p><p></p>
</body>
</html>
@app.route('/number')
def numberFilter():return render_template('number_filter.html')
4.列表相关过滤器
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>ListFilter</title>
</head>
<body>
{# 定义了一个numbers的变量 #}
{% set numbers = [2, 4, 7, 3, 5, 8] %}{# 取第一个元素 #}
<p>{{ numbers | first }}</p>{# 取最后一个元素 #}
<p>{{ numbers | last }}</p>{# 返回列表长度,可以写为count #}
<p>{{ numbers | length }}</p>{# 列表求和 #}
<p>{{ numbers | sum }}</p>{# 列表排序,默认为升序 #}
<p>{{ numbers | sort }}</p>{# 合并为字符串,返回"1 | 2 | 3 | 4 | 5" #}
<p>{{ numbers | join(' | ') }}</p>{# 列表中所有元素都全大写。这里可以用 upper,lower,但capitalize无效 #}
<p>{{ ['python','java','c++'] | upper }}</p></body>
</html>
@app.route('/list')
def numberFilter():return render_template('list_filter.html')
5.字典相关过滤器
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>DictFilter</title>
</head>
<body>
{# 定义一个user变量 #}
{% set users=[{'name':'Tom','gender':'M','age':20},
{'name':'John','gender':'M','age':18},
{'name':'Mary','gender':'F','age':24},
{'name':'Bob','gender':'M','age':31},
{'name':'Lisa','gender':'F','age':19}]
%}{# 按指定字段排序,这里设reverse为true使其按降序排 #}
<ul>
{% for user in users | sort(attribute='age', reverse=true) %}
<li>{{ user.name }}, {{ user.age }}</li>
{% endfor %}
</ul>{# 列表分组,每组是一个子列表,组名就是分组项的值 #}
<ul>
{% for group in users|groupby('gender') %}
<li>{{ group.grouper }}<ul>
{% for user in group.list %}
<li>{{ user.name }}</li>
{% endfor %}</ul></li>
{% endfor %}
</ul>{# 取字典中的某一项组成列表,再将其连接起来 #}
<p>{{ users | map(attribute='name') | join(', ') }}</p>
</body>
</html>
@app.route('/dict')
def numberFilter():return render_template('dict_filter.html')
6.自定义过滤器
from flask import Flask, render_templateapp = Flask(__name__, static_url_path='/static', static_folder='static')@app.route('/hello')
def hello_world(): # put application's code herereturn 'Hello World!'# 第一种
@app.template_filter('getTop3')
def getTop3(ls):return ls[:3] # 取列表前三个值def getSQ(ls):return list(map(lambda x: x ** 2, ls)) # 计算列表每个元素的平方# 第二种:注册一个过滤器
app.jinja_env.filters['getSQ'] = getSQ@app.route('/customFilter')
def customFilter():return render_template('custom_filter.html')if __name__ == '__main__':app.run(host='0.0.0.0', port=8888, debug=True)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>CustomFilter</title>
</head>
<body>
<h1>列表:[1, 2, 3, 4, 5, 6, 7, 8, 9]</h1> <br>
<h1>Top3:{{ [1, 2, 3, 4, 5, 6, 7, 8, 9] | getTop3 }}</h1> <br>
<h1>平方:{{ [1, 2, 3, 4, 5, 6, 7, 8, 9] | getSQ }}</h1>
</body>
</html>
五、测试器
测试器总是返回一个布尔值,它可以用来测试一个变量或者表达式,使用”is”
关键字来进行测试。
{% set name='ab' %}
{% if name is lower %}
<h2>"{{ name }}" are all lower case.</h2>
{% endif %}
测试器本质上也是一个函数,它的第一个参数就是待测试的变量,在模板中使用时可以省略去。如果它有第二个参数,模板中就必须传进去。测试器函数返回的必须是一个布尔值,这样才可以用来给if语句作判断。
1.Jinja2中内置的测试器
官网:https://jinja.palletsprojects.com/en/master/templates/#builtin-tests
tester.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Tester</title>
</head>
<body>
{# 检查变量是否被定义,也可以用undefined检查是否未被定义 #}
{% if name is defined %}<p>Name is: {{ name }}</p>
{% endif %}
{# 检查变量是否被定义,也可以用undefined检查是否未被定义 #}
{% if name is undefined %}<h1 style="color: red">变量没有定义</h1>
{% endif %}{# 检查是否所有字符都是大写 #}
{% if name is upper %}
<h2>"{{ name }}" are all upper case.</h2>
{% endif %}{# 检查变量是否为空 #}
{% if name is none %}
<h2>Variable is none.</h2>
{% endif %}{# 检查变量是否为字符串,也可以用number检查是否为数值 #}
{% if name is string %}
<h2>{{ name }} is a string.</h2>
{% endif %}{# 检查数值是否是偶数,也可以用odd检查是否为奇数 #}
{% if 2 is even %}
<h2>Variable is an even number.</h2>
{% endif %}{# 检查变量是否可被迭代循环,也可以用sequence检查是否是序列 #}
{% if [1,2,3] is iterable %}
<h2>Variable is iterable.</h2>
{% endif %}{# 检查变量是否是字典 #}
{% if {'name':'test'} is mapping %}
<h2>Variable is dict.</h2>
{% endif %}
</body>
</html>
@app.route('/tester') # tester 测试器
def tester():return render_template('tester.html')
2.自定义测试器
自定义测试器.py
import refrom flask import Flask, render_templateapp = Flask(__name__, static_url_path='/static', static_folder='static')@app.route('/')
def hello_world(): # put application's code herereturn 'Hello World!'"""自定义测试器,首先创建函数,然后注册"""@app.route('/customTester')
def customTester():return render_template('custom_tester.html')def tester_phone(phone): # 测试手机号是否合法phone_re = r'1[3-9]\d{9}'return re.match(phone_re, phone)# 两种注册测试器的方法
app.jinja_env.tests['tester_phone'] = tester_phone@app.template_test('start_with') # 注册测试器
def start_with(my_str, suffix):return my_str.lower().startswith(suffix.lower())if __name__ == '__main__':app.run(host='0.0.0.0', port=8888, debug=True)
custom_tester.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>CustomTest</title>
</head>
<body>
{#测试是否是手机号#}
{% if '1001000001' is tester_phone %}<h1>'1001' 是手机号</h1>
{% else %}<h1>'1001' 不是手机号</h1>
{% endif %}{#测试字符串是否以tester开头#}
{% if 'hello' is start_with('tester') %}<h1>'hello' 以tester开头</h1>
{% else %}<h1>'hello' 不是以tester开头</h1>
{% endif %}
</body>
</html>
六、块和继承
一般我们的网站虽然页面多,但是很多部分是重用的,比如页首,页脚,导航栏之类的。对于每个页面,都要写这些代码,很麻烦。
Flask
的Jinja2
模板支持模板继承功能,省去了这些重复代码。
1.Jinja2模板
父模板parent_template.htm
{% block temp1 %}<p>页面的头部</p><hr>
{% endblock %}<p>父模板的中间内容</p>{% block temp2 %}<hr><p>页面的而尾部</p>
{% endblock %}
子模板child_template.htm
{% extends 'parent_template.html' %}{% block temp1 %}{{ super() }}<p>子模板中的内容</p>
{% endblock %}
视图函数
@app.route('/extends')
def extends():return render_template('child_template.html')
2.include标签
这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位置。 include
标签,如果想要使用父模版中的变量,直接用就可以了。 include
的路径,也是跟 import 一样,直接从 templates
根目录下去找,不要以相对路径去找。
头部head.html
<p>页面的头部</p>
<hr>
尾部tail.html
<hr>
<p>页面的而尾部</p>
include.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Include</title>
</head>
<body>
{% include 'head.html' %}页面的中间内容{% include 'tail.html' %}
</body>
</html>
视图函数
@app.route('/include')
def include():return render_template('include.html')
七、模板中的url_for函数
模版中的 url_for
跟我们后台视图函数中的 url_for
使用起来基本是一模一样的。也是传递视图函数的名字,也可以传递参数。使用的时候,需要在 url_for
左右两边加上一个 {{ url_for('func') }}
1、动态超链接
html
页面使用如:
<a href="{{ url_for('login', user='张三', age=18) }}">登录</a>
实际上就是动态路由点击变为:
http://127.0.0.1:5000/login/%E5%BC%A0%E4%B8%89?age=18
对应的视图函数:
@app.route('/login/<user>')
def login(user):print(user)return "用户名:{}".format(user)
2、加载静态文件
静态文件:css
文件 js
文件 图片文件等文件
加载静态文件使用的是 url_for
函数。然后第一个参数需要为 static
,第二个参数需要为一个关键字参数filename='路径'
。
语法:{{ url_for("static",filename='xxx') }}
<link href="{{ url_for('static',filename='css/main.css') }}" rel="stylesheet"><script src="{{ url_for('static',filename='js/main.js') }}"></script><img src="{{ url_for('static',filename='img/main.jpg') }}"/>