一、Jinja2模板简介
-
Flask提供的 render_template 函数封装了该模板引擎
-
render_template
app = Flask(__name__,template_folder='templates')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>{{ msg }}</h1> </body> </html>
3、在视图函数设置渲染模板并设置模板数据
render_template:根据模板路径来生成html
from flask import Flask,render_template,render_template_string app = Flask(__name__) @app.route("/") def index(): """基本加载模板""" data = {} data["msg"] = "hello ,jinja2" data["title"] = "我的网页标题" # 根据模板路径来生成html ---> render_template("模板路径",**data) ret = render_template("index.html",**data) # print(type(ret)) # <class 'str'> return ret if __name__ == '__main__': app.run(debug=True)
render_template_string:根据模板内容来生成html
from flask import Flask, render_template, render_template_string app = Flask(__name__, template_folder='templates') @app.route('/') def index(): """基本加载模板""" data = {} data['msg'] = 'jiaja2,hello' data['title'] = "我的网页标题" content = """<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ msg }}</h1> </body> </html>""" # 根据模板内容来生成html ---> render_template_string("html模板代码",**data) ret=render_template_string(content,**data) print(ret,type(ret)) # 返回被渲染后的网页代码 return ret if __name__ == '__main__': app.run(host='127.0.0.1', port='5001', debug=True)
{{ }} 来表示变量名,这种 {{ }} 语法叫做 变量代码块
视图代码:
from flask import Flask, render_template, render_template_string, g app = Flask(__name__, template_folder='templates') @app.route('/') def index(): data = {} data["title"] = "hello ,jinja2" data["num_list"] = [1, 3, 5] data["book"] = {"id": 1, "title": "代码之髓", "price": 99.0} data["book_list"] = [ {"id": 1, "title": "代码之髓1", "price": 99.0}, {"id": 2, "title": "代码之髓2", "price": 99.0}, {"id": 3, "title": "代码之髓3", "price": 99.0} ] g.uname = "xikaoming" return render_template('index.html', **data) @app.route("/user/login") def login(): return "hello" if __name__ == '__main__': app.run(host='127.0.0.1', port='5001', debug=True)
返回值也可以用,直接用赋值的方法;
def index(): data_list=["a","b","c"] data_dict = { "name":"xiaoming", "id":100, } return render_template("index.html", title="我的flask项目", data_list=data_list, data_dict=data_dict )
模板代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{title}}</title> </head> <body> <h1>{{ msg }}</h1> <h1> {{book.title}}</h1> <h1>{{ book_list[1].title}}</h1> <h1>{{ book_list.0.title}}</h1> <h1>{{ num_list.0 + 10 }}</h1> </body> </html>
使用 {# #} 进行注释,注释的内容不会在html中被渲染出来
{# {{ name }} #}
你可以在自己的模板中访问一些 Flask 默认内置的函数和对象
你可以从模板中直接访问Flask当前的config对象
{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db
{{request.url}}
http://127.0.0.1
{{session.new}}
True
{{ g.name }}
{{url_for('home')}}
{{ url_for('index', post_id=1)}}
/1
from flask import Flask,render_template,render_template_string, g app = Flask(__name__) @app.route("/") def index(): """基本加载模板""" data = {} data["title"] = "hello ,jinja2" data["num_list"] = [1,3,5] data["book"] = {"id":1, "title":"代码之髓","price":99.0} data["book_list"] = [ {"id": 1, "title": "代码之髓1", "price": 99.0}, {"id": 2, "title": "代码之髓2", "price": 99.0}, {"id": 3, "title": "代码之髓3", "price": 99.0} ] g.uname = "xikaoming" ret = render_template("index.html",**data) return ret @app.route("/user/login") def login(): return "hello" if __name__ == '__main__': app.run(debug=True)
模板 templates/index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>{{ msg }}</h1> <h1>{{ book.title }}</h1> <h1>{{ book_list[1].title }}</h1> {# {{ num_list[1] + 10 }}#} {{ config.JSONIFY_MIMETYPE }} <p>{{ request.full_path }}</p> <p>{{ request.args }}</p> <p>{{ session.new }}</p> <p>{{ g.uname }}</p> <p>{{ url_for("login") }}</p> </body> </html>
files/settings/languages & frameworks/python template languages。
用 {%%} 定义的控制代码块
from flask import Flask,render_template,request from settings.dev import Config from flask_script import Manager """创建flask应用""" app = Flask(__name__,template_folder='templates') """使用脚手架[终端脚本]启动项目""" manage = Manager(app) """加载配置""" app.config.from_object(Config) @app.route("/list") def list_page(): data = {} data["book_list"] = [ {"id":1,"price":78.50,"title":"javascript入门"}, {"id":2,"price":78.50,"title":"python入门"}, {"id":3,"price":78.50,"title":"django项目实战"} ] data["name"] = int( request.args.get("name") ) return render_template("list.html",**data) if __name__ == '__main__': manage.run()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1" align="center" width="680"> <tr> <th>id</th> <th>标题</th> <th>价格</th> </tr> {# for循环 #} {% for book in book_list %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.price }}</td> </tr> {% endfor %} </table> {# 判断一个参数是否是奇数 #} {% if name % 2 == 0 %} 偶数<br> {% else %} 奇数<br> {% endif %} </body> </html>
- 我们可以在 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 循环块中你可以访问这些特殊的变量:
当前循环迭代的次数(从 1 开始) | |
loop.index0 | 当前循环迭代的次数(从 0 开始) |
loop.revindex | 到循环结束需要迭代的次数(从 1 开始) |
loop.revindex0 | 到循环结束需要迭代的次数(从 0 开始) |
loop.first | 如果是第一次迭代,为 True 。 |
loop.last | 如果是最后一次迭代,为 True 。 |
loop.length | 序列中的项目数。 |
loop.cycle |
{% 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
4.3 代码综合展示
视图函数
from flask import Flask,render_template,render_template_string, g app = Flask(__name__) @app.route("/") def index(): """流程控制""" data = {} data["num_list"] = [1,3,5] data["book"] = {"id":1, "title":"代码之髓","price":99.0} data["book_list"] = [ {"id": 11, "title": "代码之髓1", "price": 99.0}, {"id": 12, "title": "代码之髓2", "price": 99.0}, {"id": 23, "title": "代码之髓3", "price": 99.0} ] ret = render_template("list.html",**data) return ret if __name__ == '__main__': app.run(debug=True)
模板代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if request.args.name %} <p>当前地址栏上的参数有name={{ request.args.name }}</p> {% endif %} {% if request.args.debug %} <p>当前属于测试模式</p> {% else %} <p>当前属于生产模式</p> {% endif %} <p>for循环</p> <ul> {% for num in num_list %} <li>{{ num }}</li> {% endfor %} </ul> <table border="1" width="800"> <tr> <td>序号[0]</td> <td>序号[1]</td> <td>序号[倒序0]</td> <td>序号[倒序]</td> <td>ID</td> <td>Title</td> <td>Price</td> </tr> {% for book in book_list %} {% if loop.first %} <tr style="background: chartreuse;"> {% elif loop.last %} <tr style="background: orange;"> {% else %} <tr> {% endif %} <td>{{ loop.index0 }}</td> <td>{{ loop.index }}</td> <td>{{ loop.revindex0 }}</td> <td>{{ loop.revindex }}</td> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.price }}</td> </tr> {% endfor %} </table> </body> </html>
五、 过滤器
5.1 过滤器用法
过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。
使用方式:
- 过滤器的使用方式为:
{{ 变量 | 过滤器函数名(参数1,参数2,....)}}
{{ 变量 | 过滤器函数名(参数1,参数2,....) | 过滤器函数名(参数1,参数2,....) | ....}}
- 如果没有任何参数传给过滤器,则可以把括号省略掉
{{ 变量 | 过滤器函数名 }}
在 jinja2 中,过滤器是可以支持链式调用的,示例如下:
{{ "hello world" | reverse | upper }}
- 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> <p>{{ "如果x<y,z>x,那么x和z之间是否相等?" | 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 %} <p>hello world</p> <p>hello world</p> <p>hello world</p> <p>hello world</p> {% endfilter %}
-
一种是通过Flask应用对象的 add_template_filter 方法
-
重要:自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
方式一
# 自定义过滤器 def list_reverse(data): # 过滤器必须有返回值,否则模板中没有内容输出!!! return data[::-1] app.add_template_filter(list_reverse, "list_reverse")
@app.add_template_filter def list_reverse(data): return data[::-1]
- 主程序中创建和注册过滤器
from flask import Flask, render_template # 初始化 app = Flask(import_name=__name__,template_folder='templates') # 配置终端脚本运行项目 from flask_script import Manager manager = Manager(app) # 声明和加载配置 class Config(): DEBUG = True app.config.from_object(Config) # 自定义过滤器 def do_list_reverse(data): return data[::-1] # 注册过滤器 app.add_template_filter(do_list_reverse, "list_reverse") @app.route(rule='/') def index(): data={} data["user_list"] = ["xiaoming","小黑白","小红"] return render_template("index.html",**data) if __name__ == '__main__': # 运行flask manager.run()
- html调用过滤器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>title</title> </head> <body> <p>{{ user_list }}</p> <p>{{ user_list | list_reverse }}</p> <p>{{ user_list }}</p> </body> </html>
- 运行结果
['xiaoming', '小黑白', '小红']
['小红', '小黑白', 'xiaoming']
['xiaoming', '小黑白', '小红']
13812345678 ---> 138****5678
代码:
from flask import Flask,render_template,request from settings.dev import Config from flask_script import Manager """创建flask应用""" app = Flask(__name__,template_folder='templates') """使用脚手架[终端脚本]启动项目""" manage = Manager(app) """加载配置""" app.config.from_object(Config) @app.template_filter("mobile") def do_mobile(data,string): return data[:3]+string+data[7:] @app.route("/") def index(): data = {} data["user_list"] = [ {"id":1,"name":"张三","mobile":"13112345678"}, {"id":2,"name":"张三","mobile":"13112345678"}, {"id":3,"name":"张三","mobile":"13112345678"}, {"id":4,"name":"张三","mobile":"13112345678"}, ] return render_template("index2.html",**data) if __name__ == '__main__': manage.run()
index2.html,模板代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1" align="center" width="680"> <tr> <th>ID</th> <th>姓名</th> <th>手机</th> </tr> {% for user in user_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.mobile | mobile(string="****") }}</td> </tr> {% endfor %} </table> </body> </html>
效果:
-
多个模板具有完全相同的顶部和底部内容
-
多个模板中具有相同的模板代码内容,但是内容中部分值不一样
-
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
- 标签定义的内容
{% block top %} {% endblock %}
-
-
子模板使用 extends 指令声明这个模板继承自哪个模板
-
{% block top %}
顶部菜单
{% endblock top %}
{% block content %}
{% endblock content %}
{% block bottom %}
底部
{% endblock bottom %}
子模板代码:
-
extends指令声明这个模板继承自哪
{% extends 'base.html' %}
{% block content %}
需要填充的内容
{% endblock content %}
-
不支持多继承
-
为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行,而且extends必须在最上方
-
不能在一个模板文件中定义多个相同名字的block标签。否则报错
-
pip install flask_wtf
# 1. session加密的时候已经配置过了.如果没有在配置项中设置,则如下: app.secret_key = "#此处可以写随机字符串#" # 2. 也可以写在配置类中。 class Config(object): DEBUG = True SECRET_KEY = "dsad32DASSLD*13%^32" """加载配置""" app.config.from_object(Config)
2. 导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app
from flask.ext.wtf import CSRFProtect CSRFProtect(app)
3 在表单中使用 CSRF 令牌:
<form method="post" action="/"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> </form>
csrf怎么识别提交过来的csrf_token是正确的?
原理和jwt认证是一样的。
csrf_token也是token令牌的应用方式,也是分 "头部.载荷.签证"
在表单提交到服务端时,WTForms内部会直接截取提交的csrf_token的头部和载荷,并从flask中提取秘钥SECRET_KEY,生成一个新的签证,新签证与客户端提交的csrf_token签证进行字符串比较,相同则表示token没有问题。
from flask import Flask,render_template from flask_wtf import CsrfProtect app = Flask(__name__) # 开启csrf防范机制 CsrfProtect(app) class Config(object): SECRET_KEY = "dsad32DASSLD*13%^32" app.config.from_object(Config) @app.route("/") def index(): """csrf防范机制""" data = {} return render_template("form.html",**data) @app.route("/transaction",methods=["post"]) def transaction(): return "登录成功!" if __name__ == '__main__': app.run(debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="{{ url_for("transaction") }}" method="post"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> 账户: <input type="text" name="uname"><br><br> 密码: <input type="password" name="password"><br><br> <button>提交</button> </form> </body> </html>