Flask模板
模板基本语法
{% if user %} {{ user }} {% else %} hello! <ul> {% for index in indexs %} <li> {{ index }} </li> {% endfor %} </ul>
一 变量
在模板中{{ variable }}结构表示变量,是一种特殊的占位符,告诉模板引擎这个位置的值,从渲染模板时使用的数据中获取;jinja2除了能识别基本类型的变量,还能识别{};
视图:
from flask import flask, render_template app = flask(__name__) @app.route("/index") def index(): data = { "name": "test", "size": 11, "adict": {"title": "index"}, "alist": [1,2,3,4,5,6] } return render_template('index.html', **data) # return render_template("index.html", name="tile") if __name__ == '__main__': app.run()
模板:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>title</title> <meta http-equiv='content-type' content='text/htm'> </head> <body> <p>name---{{ name }}</p> <p>size---{{ size }}</p> <p>adict---title:{{ adict["title"] }}</p> <p>adict---title:{{ adict.title }}</p> <p>alist---{{ alist }}</p> <p>alist---:alist[0]:{{ alist[0] }}</p> <p>alist---:alist[2]:{{ alist.2 }}</p> <p>alist---:alist[0]+alist[1]:{{ alist[0] + alist[1] }}</p> <p>{{ "hello" + "flask" }}</p> </body> </html>
自定义错误页面:
@app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404
二 过滤器
过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,这就用到了过滤器。 过滤器的使用方式为:变量名 | 过滤器。 过滤器名写在变量名后面,中间用 | 分隔。如:{{variable | capitalize}},这个过滤器的作用:把变量variable的值的首字母转换为大写,其他字母转换为小写。 其他常用过滤器如下:
2.1 字符串过滤器:
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> trim:把值的首尾空格去掉; <p>{{ ' hello world ' | trim }}</p> reverse:字符串反转; <p>{{ 'olleh' | reverse }}</p> format:格式化输出; <p>{{ '%s is %d' | format('name',17) }}</p> striptags:渲染之前把值中所有的html标签都删掉; <p>{{ '<em>hello</em>' | striptags }}</p>
2.2 支持链式使用过滤器
<p>{{ “ hello world “ | trim | upper }}</p>
2.3 列表过滤器
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>
2.4 语句块过滤
{% filter upper %} this is a flask jinja2 introduction {% endfilter %}
2.5 自定义过滤器:
过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:一种是通过flask应用对象的add_template_filter方法。还可以通过装饰器来实现自定义过滤器。
自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
实现方式一:通过调用应用程序实例的add_template_filter方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称。
def list_step_2(li): """自定义过滤器""" return li[::2] # 注册过滤器 app.add_template_filter(list_step_2, "li2")
实现方式二:用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter("li3") def list_step_3(li): """自定义过滤器""" return li[::3]
三 web表单:
web表单是web应用程序的基本功能。
它是html页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责html页面数据采集,通过表单将用户输入的数据提交给服务器。
在flask中,为了处理web表单,我们一般使用flask-wtf扩展,它封装了wtforms,并且它有验证表单数据的功能。
wtforms支持的html标准字段
字段对象 | 说明 |
---|---|
stringfield | 文本字段 |
textareafield | 多行文本字段 |
passwordfield | 密码文本字段 |
hiddenfield | 隐藏文本字段 |
datefield | 文本字段,值为datetime.date格式 |
datetimefield | 文本字段,值为datetime.datetime格式 |
integerfield | 文本字段,值为整数 |
decimalfield | 文本字段,值为decimal.decimal |
floatfield | 文本字段,值为浮点数 |
booleanfield | 复选框,值为true和false |
radiofield | 一组单选框 |
selectfield | 下拉列表 |
selectmultiplefield | 下拉列表,可选择多个值 |
filefield | 文本上传字段 |
submitfield | 表单提交按钮 |
formfield | 把表单作为字段嵌入另一个表单 |
fieldlist | 一组指定类型的字段 |
wtforms常用验证函数
验证函数 | 说明 |
---|---|
datarequired | 确保字段中有数据 |
equalto | 比较两个字段的值,常用于比较两次密码输入 |
length | 验证输入的字符串长度 |
numberrange | 验证输入的值在数字范围内 |
url | 验证url |
anyof | 验证输入值在可选列表中 |
noneof | 验证输入值不在可选列表中 |
使用flask-wtf需要配置参数secret_key。
csrf_enabled是为了csrf(跨站请求伪造)保护。 secret_key用来生成加密令牌,当csrf激活的时候,该设置会根据设置的密匙生成加密令牌。
在html页面中直接写form表单:
#模板文件 <form method='post'> <input type="text" name="username" placeholder='username'> <input type="password" name="password" placeholder='password'> <input type="submit"> </form>
视图函数中获取表单数据:
from flask import flask,render_template,request @app.route('/login',methods=['get','post']) def login(): if request.method == 'post': username = request.form['username'] password = request.form['password'] print username,password return render_template('login.html',method=request.method)
使用flask-wtf实现表单。
pip install flask-wtf
配置参数:
app.config['secret_key'] = '.........'
模板页面:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>title</title> <meta http-equiv='content-type' content='text/htm'> </head> <body> <form method="post"> {{ reg_form.csrf_token }} {{ reg_form.username.label }} <p>{{ reg_form.username }}</p> {{ reg_form.username.errors.0 }} {{ reg_form.password.label }} <p>{{ reg_form.password }}</p> {{ reg_form.password.errors.0 }} {{ reg_form.repassword.label }} <p>{{ reg_form.repassword }}</p> {{ reg_form.repassword.errors.0 }} <br> {{ reg_form.submit }} </form> </body> </html>
视图函数:
from flask import flask, render_template, url_for, redirect, session from flask_wtf import flaskform from wtforms import stringfield, passwordfield, submitfield from wtforms.validators import datarequired, equalto app = flask(__name__) app.config["secret_key"] = "ada898u2q11joloadad02" class regiterform(flaskform): username = stringfield(label="用户名", validators=[datarequired("用户名不能为空")]) password = passwordfield(label="密码", validators=[datarequired("密码不能为空")]) repassword = passwordfield(label="确认密码", validators=[datarequired("确认密码不能为空"), equalto("password", "两次密码输入不一致")]) submit = submitfield(label="提交") @app.route("/register", methods=['get', "post"]) def register(): # 创建表单对象, 如果是post请求, 前端发送了数据,flask会把数据在构造form对象的数据,存在对象中 reg_form = regiterform() # 判断form中的数据是否合法 if reg_form.validate_on_submit(): username = reg_form.username.data password = reg_form.password.data repassword = reg_form.repassword.data session["username"] = username print(username, password, repassword) return redirect(url_for("index")) return render_template('register.html', reg_form=reg_form) @app.route("/index") def index(): username = session.get("username") return "hello %s " % username if __name__ == '__main__': app.run()
四 控制语句
4.1 if控制语句
@app.route('/user') def user(): user = 'wusong' return render_template('user.html',user=user)
<html> <head> {% if user %} <title> hello {{user}} </title> {% else %} <title> welcome to flask </title> {% endif %} </head> <body> <h1>hello world</h1> </body> </html>
4.2 for循环语句
@app.route('/loop') def loop(): fruit = ['apple','orange','pear','grape'] return render_template('loop.html',fruit=fruit)
<html> <head> {% if user %} <title> hello {{user}} </title> {% else %} <title> welcome to flask </title> {% endif %} </head> <body> <h1>hello world</h1> <ul> {% for index in fruit %} <li>{{ index }}</li> {% endfor %} </ul> </body> </html>
五 宏、继承、包含
类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
jinja2支持宏,还可以导入宏,需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复。
5.1 宏
定义宏
{% macro input() %} <input type="text" name="username" value=""> {% endmacro %}
调用宏
{{ input() }}
定义带参数的宏
{% macro input1(name, value="", type="text") %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %}
调用宏,并传递参数
{{ input1(pwd, type="password") }}
把宏单独抽取出来,封装成html文件,其它模板中导入使用
文件名可以自定义macro_input.html
{% macro function() %} <input type="text" name="username" placeholde="username"> <input type="password" name="password" placeholde="password"> <input type="submit"> {% endmacro %}
在其它模板文件中先导入,再调用
{% import 'macro_input.html' as func %} {{ func.function() }}
5.2模板继承
模板继承是为了重用模板中的公共内容。一般web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
{% block top %}.......{% endblock %}标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
父模板:base.html
{% block top %} 顶部菜单 {% endblock top %} {% block content %} {% endblock content %} {% block bottom %} 底部 {% endblock bottom %}
子模板:
{% extends 'base.html' %} {% block content %} 需要填充的内容 {% endblock content %}
模板继承使用时注意点:
-
不支持多继承。
-
为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
-
不能在一个模板文件中定义多个相同名字的block标签。
-
当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
5.3包含(include)
jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。
示例:
include的使用
{% include 'hello.html' %}
包含在使用时,如果包含的模板文件不存在时,程序会抛出templatenotfound异常,可以加上ignore missing关键字。如果包含的模板文件不存在,会忽略这条include语句。
示例:
include的使用加上关键字ignore missing
{% include 'hello.html' ignore missing %}
-
宏、继承、包含:
-
宏(macro)、继承(block)、包含(include)均能实现代码的复用。
-
继承(block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
-
宏(macro)的功能类似函数,可以传入参数,需要定义、调用。
-
包含(include)是直接将目标模板文件整个渲染出来。
-
六 flask中的特殊变量和方法
在flask中,有一些特殊的变量和方法是可以在模板文件中直接访问的。
config 对象:
config 对象就是flask的config对象,也就是 app.config 对象。 {{ config.sqlalchemy_database_uri }}
request 对象:
就是 flask 中表示当前请求的 request 对象,request对象中保存了一次http请求的一切信息。
request常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | multidict |
args | 记录请求中的查询参数 | multidict |
cookies | 记录请求中的cookie信息 | dict |
headers | 记录请求中的报文头 | environheaders |
method | 记录请求使用的http方法 | get/post |
url | 记录请求的url地址 | string |
files | 记录请求上传的文件 | * |
{{ request.url }}
url_for 方法:
url_for() 会返回传入的路由函数对应的url,所谓路由函数就是被 app.route() 路由装饰器装饰的函数。如果我们定义的路由函数是带有参数的,则可以将这些参数作为命名参数传入。
{{ url_for('index') }} {{ url_for('post', post_id=1024) }}
get_flashed_messages方法:
返回之前在flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。
{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
示例:
from flask import flask, render_template, flash app = flask(__name__) app.config["secret_key"] = "diaj29u912kjwsle01sda" flag = true @app.route("/index") def index(): global flag if flag: flash("flash1") flash("flash2") flash("flash3") flag = false return render_template("index.html") if __name__ == '__main__': app.run()
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>title</title> <meta http-equiv='content-type' content='text/htm'> </head> <body> <h1>闪现</h1> {% for msg in get_flashed_messages() %} {{ msg }} {% endfor %} </body> </html>