一、jinja2模板引擎的简介:
1.模板:
1.1视图函数的两个作用:
- 处理业务逻辑;
- 返回响应内容;
1.3 什么是模板:
- 模板其实是一个包含响应文本的文件,不是特指的html文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
- 使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
- Flask是使用 Jinja2 这个模板引擎来渲染模板
1.4 模板的好处:
- 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
- 而模板则取到视图函数的数据结果进行展示(视图展示方面)
- 代码结构清晰,耦合度低
2.jinja2模板引擎:
使用flask.render_template函数封装jinja2模板引擎.
在项目中创建templates文件夹
在pycharm中使用模板,IDE会报找不到警告
需要修改pycharm的设置,修改模板语言
2.1 基本语法:
变量:{{ 变量 }}
判断语句: {%if 表达式%} 语句1 {%elif 表达式%} 语句2 {{%else%}} 语句3 {% endif %}
循环语句: {% for foo in list_data %} 拿到数据:{{ foo }} {% endfor %}
注释语法: {# 注释 #}
代码实例:
Flask的视图代码:
# !/usr/bin python
# conding=utf-8
# 导入render_template模块
from flask import Flask, render_template
app = Flask(__name__)
# 使用模板加载index.html文件
@app.route("/")
def index():
# 定义模板数据
str = "测试一下中文的显示情况"
dict_data = {"name":"张三", "age":18}
list_data = [4,5,8,3,25,12]
return render_template("index.html",
str = str,
dict_data = dict_data,
list_data = list_data)
if __name__ == '__main__':
app.run(debug=True)
html模板文件
...
<body>
<p>传字符串:{{ str }}</p>
<hr>
<p>传字典:{{ dict_data }}</p>
<p>name: {{ dict_data["name"] }}</p>
<p>age:{{ dict_data['age'] }}</p>
<hr>
<p>传入列表: {{ list_data }}</p>
遍历列表:
<ul>
{% for i in list_data %}
<li> {{ i }}</li>
{% endfor %}
</ul>
</body>
...
页面效果
3.控制代码块:
控制代码块主要包含两个:
- if/else if /else / endif
- for / endfor
3.1 if语句:
Jinja2 语法中的if语句跟 Python 中的 if 语句相似,后面的布尔值或返回布尔值的表达式将决定代码中的哪个流程会被执行:
{%if user.is_logged_in() %}
<a href='/logout'>Logout</a>
{% else %}
<a href='/login'>Login</a>
{% endif %}
过滤器可以被用在 if 语句中:
{% if comments | length > 0 %}
There are {{ comments | length }} comments
{% else %}
There are no comments
{% endif %}
3.2 循环:
我们可以在 Jinja2 中使用循环来迭代任何列表或者生成器函数
{% for post in posts %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
循环和if语句可以组合使用,以模拟 Python 循环中的 continue 功能,下面这个循环将只会渲染post.text不为None的那些post:
{% for post in posts if post.text %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
在一个for循环中可以使用一些特殊的变量
变量 | 描述 |
loop.index | 当前循环迭代的次数(从1开始) |
loop.index0 | 当前迭代的次数(从0开始) |
loop.revindex | 到循环结束需要迭代的次数(从1开始) |
loop.revindex0 | 到循环结束需要迭代的次数(从0开始) |
loop.first | 判断循环是否为第一次迭代/td> |
loop.last | 判断循环是否为最后一次循环 |
loop.length | 序列中的项目数 |
loop.cycle/td> | 在一串序列间取值的辅助函数 |
在循环内部,你可以使用一个叫做loop的特殊变量来获得关于for循环的一些信息:
比如:要是我们想知道当前被迭代的元素序号,并模拟Python中的enumerate函数做的事情,则可以使用loop变量的index属性,例如:
{% for post in posts%}
{{loop.index}}, {{post.title}}
{% endfor %}
会输出这样的结果
1, Post title
2, Second Post
cycle函数会在每次循环的时候,返回其参数中的下一个元素,可以拿上面的例子来说明:
{% for post in posts%}
{{loop.cycle('odd','even')}} {{post.title}}
{% endfor %}
会输出这样的结果:
odd Post Title
even Second Post
3.3.代码演示:
flask视图代码:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
my_list = [
{
"id":1,
"name":"张三",
"age": 18
},
{
"id": 2,
"name": "李四",
"age": 25
},
{
"id": 3,
"name": "王五",
"age": 16
},
]
return render_template("demo04.html", my_list = my_list)
if __name__ == '__main__':
app.run(debug=True)
模板代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板的基本控制语句</title>
</head>
<body>
{% for ls in my_list %}
{% if loop.index0 == 0 %}
<li style="background-color: orange">{{ ls.name }}</li>
{% elif loop.index0 == 1 %}
<li style="background-color: green">{{ ls.name }}</li>
{% elif loop.index0 == 2 %}
<li style="background-color: red">{{ ls.name }}</li>
{% endif %}
{% endfor %}
</body>
</html>
页面效果:
二、 过滤器:
- 过滤器的本质是函数
- 过滤器的作用是格式化输出数据;
- 过滤器在模板中使用
- 模板默认状态下会自动转义,把html标签当成原始字符串进行输出:
- 过滤器可以多个连用,支持链式调用;
4.1 语法:
{{ 变量|过滤器 }}
4.2 字符串处理:
- safe:禁用转义(模板默认情况下开启转义), 在确保数据的安全的情况下可以使用safe
- capitalize:首字符大写,其余小写
- lower:转小写
- upper:转大写
- reverse:字符串反转
...
<body>
{# 过滤器对字符串的处理 #}
<p>当前字符串: {{ "bAnK OF cHiNA" }}</p>
<p>字符串反转: {{ "bAnK OF cHiNA" | reverse}}</p>
{# 多个过滤器可以连用 #}
<p>字符串首字母大写再反转: {{ "bAnK OF cHiNA" | capitalize |reverse }}</p>
<p>首字母大写:{{ "bAnK OF cHiNA" | capitalize }}</p>
<p>全部大写: {{ "bAnK OF cHiNA" | upper }}</p>
<p>全部小写:{{ "bAnK OF cHiNA" | lower }}</p>
<hr>
{# 模板默认情况下,会开启转义,把html标签当成原始字符串进行输出,所以模板不能识别html标签 #}
<p>斜体默认输出:{{ "<em>bAnK OF cHiNA</em>" }}</p>
<p>斜体使用sate:{{ "<em>bAnK OF cHiNA</em>" | safe }}</p>
<hr>
</body>
...
页面效果:
4.3 列表的处理:
- last:取最后一个值;
- first: 取第一个值;
- sum :求和;
- length: 长度
- sort:排序
...
<body>
{# 过滤器对列表的处理 #}
<p>默认列表输出:{{ [4,2,3,1,5] }}</p>
<p>列表first输出:{{ [4,2,3,1,5] | first }}</p>
<p>列表last输出:{{ [4,2,3,1,5] | last }}</p>
<p>列表sum输出:{{ [4,2,3,1,5] | sum }}</p>
<p>列表sort输出:{{ [4,2,3,1,5] | sort }}</p>
<p>列表length输出:{{ [4,2,3,1,5] | length }}</p>
</body>
...
页面效果:
4.4 自定义过滤器:
自定义过滤器在视图中实现;
如果和内置的过滤器重名,会覆盖内置的过滤器
模板代码:
...
<body>
{# 使用自定义装饰器 #}
<p>列表默认输出:{{ [4,2,3,1,5] }}</p>
<p>列表反转输出:{{ [4,2,3,1,5] | list_filter }}</p>
<p>列表反转输出:{{ [4,2,3,1,5] | list_filter2 }}</p>
</body>
...
Flask视图函数代码:
...
# 自定义过滤器:实现列表数据的反转
def list_filter(ls):
# 把接受的参数转换成列表
temp_list = list(ls)
temp_list.reverse()
return temp_list
# 方法一:添加自定义过滤器给模板
# 第一个参数表示自定义过滤器的函数名,第二个参数表示过滤器的名称,如果和内置过滤器重名,会覆盖内置过滤器
app.add_template_filter(list_filter, "list_filter")
...
...
# 方法二:以装饰器的形式实现自定义过滤器
@app.template_filter("list_filter2")
def list_filter2(ls):
# 把接受的参数转换成列表
temp_list = list(ls)
temp_list.reverse()
return temp_list
...
三、模板代码的复用:
1、继承:
1.1 继承
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
- 标签定义的内容
{% block top %} {% endblock %} - 相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
- 子模板使用 extends 指令声明这个模板继承自哪个模板
- 父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()
- 父类模板中定义子类共有的内容,子类模板中完成本类模板中特有的内容
代码实现:
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父模板{% endblock %}的样式</title>
</head>
<body>
{% block top %}
<p>这是基模板的头部实现</p>
{% endblock %}
{% block body %}
<p>这是基模板的内容实现</p>
{% endblock %}
{% block footer %}
<p>这是基模板的底部实现...</p>
{% endblock %}
</body>
</html>
继承的模板.html
{% extends "base.html" %}
{% block title %}继承模板{% endblock %}
{% block top %}
<p>这是新的继承模板的top</p>
{% endblock%}
Flask视图函数:
#!/usr/bin/env python3
# coding=utf-8
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("demo06_extends.html")
if __name__ == '__main__':
app.run(debug=True)
页面效果:
1.2模板继承使用时注意点:
- 不支持多继承;
- 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行
- 不能在一个模板文件中定义多个相同名字的block标签
- 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好.
2.宏(macro):
2.1 宏的理解:
- 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串
- 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用
- 需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复
- 当模板页面需要封装动态的功能代码时,使用宏
2.2 宏的使用:
宏的定义:
{% macro func([参数列表]) %}
样式实现
{% endmacro %}
宏的调用:
{{ func([实参]) }}
导入宏:
{% import "外部宏的模板文件" as 模板文件别名 %}
2.3 代码实例:
内部定义、内部调用无参宏:
...
<body>
{# 定义宏 #}
{% macro func() %}
<input type="text" name="User" placeholder="username" size="30"> <br>
<input type="password" name="Password" placeholder="password" size="30"> <br>
<input type="submit"> <br><hr>
{% endmacro %}
{# 调用宏 #}
{{ func() }}
</body>
...
内部定义、内部调用有参宏:
...
<body>
{# 调用宏 #}
{{ func() }}
{# 定义带参数的宏函数 #}
{% macro func1(type1, type2, size) %}
<input type="{{ type1 }}" name="User" placeholder="username" size="{{ size }}"> <br>
<input type="{{ type2 }}" name="Password" placeholder="password" size="{{ size }}"> <br>
<input type="submit"> <br><hr>
{% endmacro %}
{# 调用宏 #}
{{ func1("password", "password", 20) }}
</body>
...
从外部引用宏文件.html
{% macro fun(type,size) %}
<input type="{{ type }}" name="User" placeholder="username" size="{{ size }}"> <br>
<input type="{{ type }}" name="Password" placeholder="password" size="{{ size }}"> <br>
<input type="submit"> <br>
{% endmacro %}
引用宏的模板:
...
<body>
{# 导入宏 #}
{% import 'temp_macro.html' as f %}
{# 从导入的文件中调用宏 #}
{{ f.fun('text','20') }}
</body>
页面实现效果:
3.包含:
3.1 在模板中,可能会遇到以下情况:
- 多个模板具有完全相同的顶部和底部内容
- 多个模板中具有相同的模板代码内容,但是内容中部分值不一样
- 多个模板中具有完全相同的 html 代码块内容
包含直接复用整个页面
3.2 代码实例:
模板文件.html
{% include "base.html" %}
这种写法没有做"文件不存在忽略"的处理,如果包含的文件不存在,jinja2引擎报错:
加入忽略包含的文件不存在参数,
{% include "base222.html" ignore missing %}
jinja2引擎不会报错,包含的内容为空,且响应码正确
四、CSRF(跨站请求伪造):
1 什么是CSRF: 钓鱼
- CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。
- CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。
- 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…
- 造成的问题:个人隐私泄露以及财产安全。