flask
flask基本使用
一、flask安装以及启动
1、安装
pip install flask
2、开启第一个flask项目
新建文件HelloWord.py
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World' if __name__ == '__main__': app.run()
3、参数配置
1)从配置对象中加载,创建配置的类 app.config.from_object("类名")
from flask import Flask class Config(object): DEBUG = True # 创建 Flask 类的对象,指向程序所在的包的名称 app = Flask(__name__) # 从配置对象中加载配置 app.config.from_object(Config) if name == "__main__" app.run()
2)创建配置文件 config.ini,并在配置文件中添加配置 在项目文件夹下创建文本,重命名成config.ini app.config.from_pyfile("文件名")
DEBUG = True
在flask文件中调用配置文件
from flask import Flask app = Flask(__name__) # 从配置文件中加载配置 app.config.from_pyfile('config.ini') if name == "__main__" app.run()
4、路由
指定路由地址
from flask import Flask app = Flask(__name__) # 指定访问路径为 demo1 @app.route('/demo1') def demo1(): return 'demo1' if name=="__main__" app.run(debug = True)
通过路由传参数
通过路由传递用户ID,来取到对应用户的信息。路由传递的参数默认当做 string 处理,也可以指定参数的类型
from flask import Flask app = Flask(__name__) # 路由传递参数 @app.route('/user/<int:user_id>') def user_info(user_id): return 'hello %d' % user_id if name=="__main__" app.run(debug = True)
指定参数的类型
from flask import Flask app = Flask(__name__) # 指定路由传递参数类型 @app.route('/user/<int:user_id>') def user_info(user_id): return 'hello %d' % user_id if __name__ == '__main__': app.run(debug=True)
指定请求方式 methods
from flask import Flask app = Flask(__name__) @app.route('/demo2', methods=['GET', 'POST']) def demo2(): # 直接从请求中取到请求方式并返回 return request.method if name == "__main" app.run(debug = True)
正则匹配路由
使用场景:限制用户访问,只有符合规则的路由参数才能访问(可以用来限流)
实现步骤:
1)导入转换器基类:在 Flask 中,所有的路由的匹配规则都是使用转换器对象进行记录
2)自定义转换器:自定义类继承于转换器基类
3)添加转换器到默认的转换器字典中
4)使用自定义转换器实现自定义匹配规则
from flask import Flask #导入转换器基类 from werkzeug.routing import BaseConverter app = Flask(__name__) # 自定义正则转换器 class RegexConverter(BaseConverter): def __init__(self, url_map, *args): super(RegexConverter, self).__init__(url_map) # 将接受的第1个参数当作匹配规则进行保存 self.regex = args[0] # 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re app.url_map.converters['re'] = RegexConverter # 使用转换器去实现自定义匹配规则 # 当前此处定义的规则是:3位数字 @app.route('/user/<re("[0-9]{3}"):user_id>') def user_info(user_id): return "user_id 为 %s" % user_id if __name__ == '__main__': app.run(debug=True)
5、视图
获取请求的参数
返回json数据
#jsonify返回的数据类型是Content-Type: application/json @app.route('/demo4') def demo4(): json_dict = { "user_id": 10, "user_name": "laowang" } return jsonify(json_dict) #json.dumps返回的数据类型是Content-Type: text/html; charset=utf-8 @app.route("/demo5") def demo4(): json_dict = { "user_id": 10, "user_name": "laowang" } return json.dump(json_dict)
重定向
# 重定向 @app.route('/demo1') def demo1(): return 'demo1' #redirect直接是url,就是app.route的路径参数。 @app.route('/demo2') def demo2(): return redirect("www.baidu.com") # url_for()是对函数进行操作。 @app.route('/demo5') def demo5(): return redirect(url_for('demo1')) #url_for()对带参数的函数进行操作 # 路由传递参数 @app.route('/user/<int:user_id>') def user_info(user_id): return 'hello %d' % user_id # 重定向 @app.route('/demo5') def demo5(): # 使用 url_for 生成指定视图函数所对应的 url return redirect(url_for('user_info', user_id=100))
6、异常捕获
from flask import Flask,jsonify,abort app = Flask(__name__) #设置返回的错误信息 @app.errorhandler(500) def internal_server_error(e): return '服务器搬家了' @app.route('/users/<user_id>') def users(user_id): if int(user_id) == 1: data = {"result":200,"msg":"success"} return jsonify(data) else: abort(500) if __name__ == '__main__': app.run(debug=True)
7、钩子函数
请求钩子是通过装饰器的形式实现,Flask支持四种请求钩子;
before_first_request: 在处理第一个请求前执行
before_request: 在每次请求前执行 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
after_request: 如果没有抛出错误,在每次请求后执行
teardown_request:在每次请求后执行 接受一个参数:错误信息,如果有相关错误抛出
from flask import Flask,abort,jsonify app = Flask(__name__) #可以带动一个异步执行的函数,进行一些健康指标的检查,如果发现有异常,则截断后续的请求,将整个Flask应用停止。 @app.before_first_request def before_first_request(): print("before_first_request") #共享session的鉴权函数、请求黑白名单过滤、根据endpoint进行请求j等 @app.before_request def before_request(): print("before_request") #一般用于格式化响应结果,包括响应请求头,响应的格式等。 @app.after_request def after_request(response): print("after_request") response.headers["Content-Type"] = "application/jsonss" return response #销毁DB连接等 @app.teardown_request def teardown_request(e): print("teardown_request") @app.route("/") def index(): data = {"result": 200, "msg": "success"} return jsonify(data) if __name__ == '__main__': app.run(debug=True) #在第1次请求时的打印: #before_first_request #before_request #after_request #teardown_request #在第2次请求时的打印: #before_request #after_request #teardown_request
二、flask的模板
1、基本使用
在项目下创建 templates
文件夹,用于存放所有的模板文件,并在目录下创建一个模板html文件 temp_demo1.html
设置 templates 文件夹属性以便能够在代码中有智能提示
设置 html 中的模板语言,以便在 html 有智能提示
创建视图函数,将该模板内容进行渲染返回
@app.route('/') def index(): return render_template('temp_demo1.html')
一个例子
python代码
@app.route('/') def index(): # 往模板中传入的数据 my_str = 'Hello World' 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 )
html代码
<!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 World #10 #[3, 4, 2, 1, 7, 9] #{'name': 'xiaoming', 'age': 18}
2、过滤器
过滤器的本质就是函数
有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。
a、使用方式:
1)过滤器的使用方式为:变量名 | 过滤器。
{{ variable | filter_name(*args) }}
2)如果没有任何参数传给过滤器,则可以把括号省略掉
{{ variable | filter_name }}
3)在 jinja2 中,过滤器是可以支持链式调用的
{{ "hello world" | reverse | upper }}
b、常见内置过滤器
字符串操作
safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
hello
capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>
Hello
lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>
hello
upper:把值转成大写
<p>{{ 'hello' | upper }}</p>
HELLO
title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>
Hello
reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>
hello
format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>
name is 17
striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>
hello
truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>
hello...
列表操作
first:取第一个元素 1
<p>{{ [1,2,3,4,5,6] | first }}</p>
last:取最后一个元素 6
<p>{{ [1,2,3,4,5,6] | last }}</p>
length:获取列表长度 21
<p>{{ [1,2,3,4,5,6] | length }}</p>
sum:列表求和 6
<p>{{ [1,2,3,4,5,6] | sum }}</p>
sort:列表排序 [1,2,3,4,5,6]
<p>{{ [6,2,3,1,5,4] | sort }}</p>
语句过滤
{% filter upper %}
#一大堆文字#
{% endfilter %}
自定义过滤器
过滤器的本质是函数。
当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:
1)一种是通过Flask应用对象的 add_template_filter 方法
2)通过装饰器来实现自定义过滤器
重要:自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
自定义列表反转过滤器
#方式一 #通过调用应用程序实例的 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
使用列表反转过滤器
<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]
3、控制代码模块
控制代码块主要包含两个:
1) if/else if /else / endif
Jinja2 语法中的if语句跟 Python 中的 if 语句相似,后面的布尔值或返回布尔值的表达式将决定代码中的哪个流程会被执行
#基本使用 {%if user.id == 1 %} <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 %}
2) for / endfor
在 Jinja2 中使用循环来迭代任何列表或者生成器函数
#for基本使用 {% for post in posts %} <div> <h1>{{ post.title }}</h1> <p>{{ post.text | safe }}</p> </div> {% endfor %} #for和if联合使用 {% for post in posts if post.text %} <div> <h1>{{ post.title }}</h1> <p>{{ post.text | safe }}</p> </div> {% endfor %}
for循环中一些特殊的变量
cycle函数会在每次循环的时候,返回其参数中的下一个元素,
#my_array = [3, 4, 2, 1, 7, 9] {% for post in my_array %} {# <p>{{loop.index}}, {{post}}</p>#} <p>{{ loop.cycle("old",'even',"eoirj","rfd") }},{{post}}</p> {% endfor %} #显示结果 #old,3 #even,4 #eoirj,2 #rfd,1 #old,7 #even,9
demo:
{% for item in my_list if item.id != 5 %} {% if loop.index == 1 %} <li style="background-color: orange">{{ item.value }}</li> {% elif loop.index == 2 %} <li style="background-color: green">{{ item.value }}</li> {% elif loop.index == 3 %} <li style="background-color: red">{{ item.value }}</li> {% else %} <li style="background-color: purple">{{ item.value }}</li> {% endif %} {% endfor %}
from flask import Flask,render_template,redirect,url_for app = Flask(__name__) my_list = [ { "id": 1, "value": "我爱工作" }, { "id": 2, "value": "工作使人快乐" }, { "id": 3, "value": "沉迷于工作无法自拔" }, { "id": 4, "value": "日渐消瘦" }, { "id": 5, "value": "以梦为马,越骑越傻" } ] @app.route('/') def index(): return render_template('tmp.html', my_list= my_list) if __name__ == '__main__': app.run(debug=True)
4、模板代码复用
宏 macro
1)把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串
2)为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用
3)需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复
{#原始代码#} <form> <label>用户名:</label><input type="text" name="username"><br/> <label>身份证号:</label><input type="text" name="idcard"><br/> <label>密码:</label><input type="password" name="password"><br/> <label>确认密码:</label><input type="password" name="password2"><br/> <input type="submit" value="注册"> </form> {#使用宏的代码#} {#定义宏,相当于定义一个函数,在使用的时候直接调用该宏,传入不同的参数就可以了#} {% macro input(label="", type="text", name="", value="") %} <label>{{ label }}</label><input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %} {#使用宏渲染注册表单#} <form> {{ input("用户名:", name="username") }}<br/> {{ input("身份证号:", name="idcard") }}<br/> {{ input("密码:", type="password", name="password") }}<br/> {{ input("确认密码:", type="password", name="password2") }}<br/> {{ input(type="submit", value="注册") }} </form>
继承
模板继承是为了重用模板中的公共内容。
继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
1)相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
2)子模板使用 extends 指令声明这个模板继承自哪个模板
3)父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()
父模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {# 第一个block 标题#} <title>{% block titleBlock %}默认标题{% endblock %}</title> <link rel="stylesheet" type="text/css" href="../static/css/reset.css"> <link rel="stylesheet" type="text/css" href="../static/css/main.css"> <script type="text/javascript" src="../static/js/jquery-1.12.4.min.js"></script> <script type="text/javascript" src="../static/js/main.js"></script> {# 第二个block 里面装js导入路径#} {% block scriptBlock %} {% endblock %} </head> <body> <div class="header_con"> <div class="header"> <a href="#" class="logo fl"><img src="../static/images/logo.png" alt="logo"></a> {# 第四个block top栏#} {% block topbarBlock %} {% endblock %} <div class="user_btns fr"> <a href="javascript:;" class="login_btn">登录</a> / <a href="javascript:;" class="register_btn">注册</a> </div> <!-- 用户登录后显示下面,隐藏上面 --> <div class="user_login fr"> <img src="../static/images/person01.png" class="lgin_pic"> <a href="#">用户张三</a> <a href="#">退出</a> </div> </div> </div> <div class="conter_con"> {# 第三个block 里面装这内容页面#} {% block contentBlock %} {% endblock %} <div class="rank_con fr"> {# 第五个block 作者信息#} {% block authorBlock %} {% endblock %} <div class="rank_title"> <h3>点击排行</h3> </div> <ul class="rank_list"> <li><span class="first">1</span><a href="#">势如破竹!人民币再度连闯四道关口 在岸、离岸双双升破6.42</a></li> <li><span class="second">2</span><a href="#">凛冬已至,还有多少银行人在假装干银行</a></li> <li><span class="third">3</span><a href="#">人民日报:部分城市楼市放松限制引关注,楼市调控不会“拉抽屉”</a></li> <li><span>4</span><a href="#">势如破竹!人民币再度连闯四道关口 在岸、离岸双双升破6.42</a></li> <li><span>5</span><a href="#">凛冬已至,还有多少银行人在假装干银行</a></li> <li><span>6</span><a href="#">人民日报:部分城市楼市放松限制引关注,楼市调控不会“拉抽屉”</a></li> </ul> </div> </div> <div class="footer"> <div class="footer_links"> <a href="">关于我们</a> <span>|</span> <a href="">联系我们</a> <span>|</span> <a href="">招聘人才</a> <span>|</span> <a href="">友情链接</a> </div> <p class="copyright"> CopyRight © 2018 新经资讯信息技术有限公司 All Rights Reserved<br /> 电话:010-****888 京ICP备*******8号 </p> </div> <!-- 登录表单 --> <form class="login_form_con"> <div class="login_form"> <div class="login_title"> <h3>登 录</h3> <a href="javascript:;" class="shutoff"></a> </div> <div class="form_group"> <input id="mobile" type="text" name="mobile" autocomplete="off"> <div class="input_tip">手机号</div> <div id="login-mobile-err" class="error_tip">手机号不能为空</div> </div> <div class="form_group"> <input id="password" type="password" name="password"> <div class="input_tip">密码(不少于6位)</div> <div id="login-password-err" class="error_tip">密码不能为空</div> </div> <input type="submit" name="" value="登 录" class="input_sub"> <div class="down_link">还没有账号?<a href="javascript:;" class="to_register">立即注册</a></div> </div> <div class="mask"></div> </form> <!-- 注册表单 --> <form class="register_form_con"> <div class="register_form"> <div class="register_title"> <h3>注 册</h3> <a href="javascript:;" class="shutoff"></a> </div> <div class="form_group"> <input type="text" name="mobile" autocomplete="off" id="register_mobile" class="phone_input"> <div class="input_tip">手机号</div> <div id="register-mobile-err" class="error_tip">手机号不能为空</div> </div> <div class="form_group"> <input type="text" name="code_pwd" id="imagecode" class="code_pwd"> <div class="input_tip">图形验证码</div> <img src="../static/images/pic_code.png" class="get_pic_code" onclick="generateImageCode()"> <div id="register-image-code-err" class="error_tip">图形码不能为空</div> </div> <div class="form_group"> <input type="text" name="smscode" id="smscode" class="code_pwd"> <div class="input_tip">手机验证码</div> <a href="javascript:;" class="get_code" onclick="sendSMSCode()">点击获取验证码</a> <div id="register-sms-code-err" class="error_tip">验证码不能为空</div> </div> <div class="form_group"> <input type="password" name="password" id="register_password" class="pass_input"> <div class="input_tip">密码(不少于6位)</div> <div id="register-password-err" class="error_tip">密码不能为空</div> </div> <div class="form_group2 clearfix"> <input type="checkbox" class="agree_input" checked> <p>同意使用条款,并已阅读"跟帖评论自律管理承诺书"</p> <div class="error_tip">请勾选</div> </div> <input type="submit" name="" value="注 册" class="input_sub"> <div class="down_link">已有账号?<a href="javascript:;" class="to_login">立即登录</a></div> </div> <div class="mask"></div> </form> </body> </html>
子模板
{#1.继承 base.html#} {% extends "base.html" %} {#2.重写titleblock#} {% block titleBlock %} 首页-新经资讯 {% endblock %} {#3.重写scriptblock#} {% block scriptBlock %} <script type="text/javascript" src="../static/js/index.js"></script> {% endblock %} {#4.重写contentblock#} {% block contentBlock %} <ul class="list_con fl"> <li> <a href="#" class="news_pic fl"><img src="../static/images/news_pic.jpg"></a> <a href="#" class="news_title fl">日本史上最大IPO之一要来了:软银计划将手机业务分拆上市软银计划将手机业务分拆上市</a> <a href="#" class="news_detail fl">据日经新闻网,软银计划让旗下核心业务移动手机部门SoftBank Corp.分拆上市,或募资2万亿日元(约180亿美元)。随着软银逐步向投资公司转型,此举旨在给手机业务部门更多自主权。</a> <div class="author_info fl"> <div class="author fl"> <img src="../static/images/person.png" alt="author"> <a href="#">乐鸣</a> </div> <div class="time fl">2017-01-01 00:00:00</div> </div> </li> <li> <a href="#" class="news_pic fl"><img src="../static/images/news_pic.jpg"></a> <a href="#" class="news_title fl">日本史上最大IPO之一要来了:软银计划将手机业务分拆上市</a> <a href="#" class="news_detail fl">据日经新闻网,软银计划让旗下核心业务移动手机部门SoftBank Corp.分拆上市,或募资2万亿日元(约180亿美元)。随着软银逐步向投资公司转型,此举旨在给手机业务部门更多自主权。</a> <div class="author_info fl"> <div class="source fl"> 来源:广州日报 </div> <div class="time fl">2017-01-01 00:00:00</div> </div> </li> <li> <a href="#" class="news_pic fl"><img src="../static/images/news_pic.jpg"></a> <a href="#" class="news_title fl">日本史上最大IPO之一要来了:软银计划将手机业务分拆上市</a> <a href="#" class="news_detail fl">据日经新闻网,软银计划让旗下核心业务移动手机部门SoftBank Corp.分拆上市,或募资2万亿日元(约180亿美元)。随着软银逐步向投资公司转型,此举旨在给手机业务部门更多自主权。</a> <div class="author_info fl"> <div class="author fl"> <img src="../static/images/person.png" alt="author"> <a href="#">乐鸣</a> </div> <div class="time fl">2017-01-01 00:00:00</div> </div> </li> <li> <a href="#" class="news_pic fl"><img src="../static/images/news_pic.jpg"></a> <a href="#" class="news_title fl">日本史上最大IPO之一要来了:软银计划将手机业务分拆上市</a> <a href="#" class="news_detail fl">据日经新闻网,软银计划让旗下核心业务移动手机部门SoftBank Corp.分拆上市,或募资2万亿日元(约180亿美元)。随着软银逐步向投资公司转型,此举旨在给手机业务部门更多自主权。</a> <div class="author_info fl"> <div class="source fl"> 来源:广州日报 </div> <div class="time fl">2017-01-01 00:00:00</div> </div> </li> </ul> {% endblock %} {% block topbarBlock %} <ul class="menu fl"> <li class="active" data-cid="0"><a href="javascript:;">最新</a></li> <li data-cid="1"><a href="javascript:;">股市</a></li> <li data-cid="2"><a href="javascript:;">债市</a></li> <li data-cid="3"><a href="javascript:;">商品</a></li> <li data-cid="4"><a href="javascript:;">外汇</a></li> <li data-cid="5"><a href="javascript:;">公司</a></li> </ul> {% endblock %}
继承使用时注意点:
1)不支持多继承
2)为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行
3)不能在一个模板文件中定义多个相同名字的block标签。
4)当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
包含
将另一个模板整个加载到当前模板中,并直接渲染。
1)include的使用
{% include 'hello.html' %}
包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing 关键字。如果包含的模板文件不存在,会忽略这条include语句。
2)include 的使用加上关键字ignore missing
{% include 'hello.html' ignore missing %}
5、模板中的特有变量
可以在自己的模板中访问一些 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 }} anndy
url_for()
url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接: {{url_for('home')}} /
如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中: {{ url_for('post', post_id=1)}} /post/1
get_flashed_messages()
这个函数会返回之前在flask中通过flask()传入的消息的列表,flash函数的作用很简单,可以把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:
{%for message in get_flashed_messages()%}
{{message}}
{%endfor%}
6、Flask-WTF表单
Web表单
Web 表单是 Web 应用程序的基本功能。
它是HTML页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML页面数据采集,通过表单将用户输入的数据提交给服务器。
在Flask中,为了处理web表单,我们可以使用 Flask-WTF 扩展,它封装了 WTForms,并且它有验证表单数据的功能
WTForms支持的HTML标准字段
WTForms常用验证函数
注意:
使用 Flask-WTF 需要配置参数 SECRET_KEY。
CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。
from flask import Flask,render_template, flash,request #导入wtf扩展的表单类 from flask_wtf import FlaskForm #导入自定义表单需要的字段 from wtforms import SubmitField,StringField,PasswordField #导入wtf扩展提供的表单验证器 from wtforms.validators import DataRequired,EqualTo app = Flask(__name__) app.config['SECRET_KEY']='SECRET_KEY' app.config['WTF_CSRF_ENABLED'] = False #自定义表单类,文本字段、密码字段、提交按钮 class RegisterForm(FlaskForm): username = StringField("用户名:", validators=[DataRequired("请输入用户名")], render_kw={"placeholder": "请输入用户名"}) password = PasswordField("密码:", validators=[DataRequired("请输入密码")]) password2 = PasswordField("确认密码:", validators=[DataRequired("请输入确认密码"), EqualTo("password", "两次密码不一致")]) submit = SubmitField("注册") #定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证 @app.route('/demo1', methods=["get", "post"]) def demo2(): register_form = RegisterForm() # 验证表单 if register_form.validate_on_submit(): # 如果代码能走到这个地方,那么就代码表单中所有的数据都能验证成功 username = request.form.get("username") password = request.form.get("password") password2 = request.form.get("password2") # 假装做注册操作 print(username, password, password2) return "success" else: if request.method == "POST": flash("参数有误或者不完整") return render_template('wtfuser.html', form=register_form) if __name__ == '__main__': app.run(debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flask-WTF</title> </head> <body> <form method="post"> {{ form.username.label }} {{ form.username }}<br/> {{ form.password.label }} {{ form.password }}<br/> {{ form.password2.label }} {{ form.password2 }}<br/> {{ form.submit }} </form> {% for message in get_flashed_messages() %} {{ message }} {% endfor %} </body> </html>