5、web表单、重定向和用户会话、flask消息
**web表单的视图函数和渲染**
1、安装Flask-WTF
pip install flask-wtf
附:
Flask-WTF插件介绍
flask-wtf是flask框架的表单验证模块,可以很方便生成表单,也可以当做json数据交互的验证工具,支持热拔插
flask_wtf完全使用wtfforms组件的字段模型,wtforms对字段的定义在fields模块;又分为core和simple,core模块定义了普通使用的字段,simple在core模块的基础上扩展了一些字段,这些字段会自动进行字段级别的效验
常用字段说明:
#core.py
BooleanField:布尔类型,如Flask,True
StringField:字符串类型
DecimalField:小数点文本字段,如‘1.23’
DateField:日期字段,格式‘%Y-%m-%d’
DateTimeField:日期字段,格式 '%Y-%m-%d %H:%M:%S'
FieldList:统一字段类型组成列表 如:FieldList(StringField('Name', [validators.required()]))
FloatField:浮点数类型
IntegerField:整型
SelectMultipleField:多选框
RadioField:单选框
#simple.py
TextAreaField:文本域,可接受多行输入
PasswordField:密码字段,输入的不会直接在浏览器明文显示
FileField:上传文件,但不会处理验证文件,需要手动处理
HiddenField:隐藏字段
SubmitField:按钮
TextField:字符串类型的别名,弃用
字段的验证序列
字段的参数validators可以指定提交表单的验证序列,按照从左到右的顺序,默认的可选验证在wtforms.validators模块中,已经封装的验证方法有:
DataRequired/data_required:验证数据是否真实存在,即不能为空,必须是非空白字符串,否则触发StopValidation错误。
InputRequired/input_required:和DataRequired的区别在于可以是空白字符串;
Required/required:data_required的别名
Email/email:验证符合邮件的格式,只有最基本的验证;
EqualTo/equal_to:比较两个字段的值,比如密码和确认密码,如果不相等就触发错误,equal_to(field,message),需要输入另一个字段的名字。
IPAddress/ip_address:验证是否是ip地址,默认验证IPV4地址。
MacAddress/mac_address:验证是否符合mac格式;
UUID:是否是uuid格式;
URL/url:验证是否符合url格式;
Regexp/regexp:用提供的正则表达式验证字段;Regexp(r"")
Length/length:设置字段值的长度,Length(min,max);
NumberRange/number_range:设置一个数字字段的取值范围,可以针对浮点数和小数;NumberRange(min,max)
Optional/optional:允许字段为空并停止验证;
NoneOf/none_of:将传入的数据和无效的比较,是抛出异常;Noneof(values).
Anyof/any_of:将传入的数据和预设的数据比较,不是异常。Anyof(values).
示例:
导入相应模块
NameForm表单中有一个名为name的文本字段和一个名为submit的提交按钮
StringField类表示属性为type="text"的< input>函数
SubmitField类表示属性为type="submit"的< input>元素
字段构造函数的第一个参数把表单渲染成HTML时使用的标号
验证函数Required()确保提交的字段不为空
实例:
1、目录结构
2、在hello.py中修改视图函数,使得能够处理表单
from flask import Flask,request,make_response,redirect,render_template from flask_bootstrap import Bootstrap from flask_wtf import Form from wtforms import StringField,SubmitField from wtforms.validators import Required app = Flask(__name__) app.config["SECRET_KEY"] = "123456" bootstrap = Bootstrap(app) class NameForm(Form): name = StringField('what is your name?', validators=[Required()]) submit = SubmitField('Submit') @app.route('/', methods=['GET', 'POST']) def index(): name = None form = NameForm() if form.validate_on_submit(): name = form.name.data form.name.data = '' return render_template('index.html', form=form, name=name) @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 @app.errorhandler(500) def internal_server_error(e): return render_template('500.html'), 500 @app.route('/user/<name>') def user(name): return render_template('user.html', name=name) @app.route('/user/<id>') def get_user(id): user = load_user(id) if not user: abort(404) return '<h1>Hello, %s</h1>' % user.name if __name__ == '__main__': bootstrap.run()
3、在templates/index.html中使用Flask-WTF和Flask-Bootstrap渲染表单
模板现在分为两部分,第一部分是页面头部,显示欢迎消息
Jinja2中的条件语句格式为{%if condition%}...{% else %}...{% endif %}
如果条件为True,那么渲染if和else指令之间的值。如果条件的计算结果为False,则渲染else和endif之间的值
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf%} {% block title %}Flasky{% endblock %} {% block page_content %} <div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}</h1> </div> {{ wtf.quick_form(form)}} {% endblock %}
4、base.html
{% extends "bootstrap/base.html" %} {% block title %}Flasky{% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flask</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> <div class="page-header"> {% block page_content %}{% endblock %} </div> </div> {% endblock %}
5、结果
当提交空表单时,Required()验证函数会捕获这个错误
**重定向和用户会话**
当提交名字后,再重新刷新页面,会出现告警
处理该告警方式之一是保证最后一次提交是get请求,会出现的问题是,程序处理POST请求时,使用form.name.data获取用户输入的名字,可是一旦这个请求结束了,数据也就丢失了。因此在重定向时,需要保存这个输入的名字,可以使用session
总结:
该能的基本实现流程包括 :通过WTForms插件创建form表单,有一个文本框和提交按钮;通过Flask-Bootstrip插件将form表单渲染为HTML页面;为了访问这个页面,通过定义视图函数建立后台表单处理逻辑,返回HTML页面所需参数
1、修改hello.py,重定向和用户会话
推荐使用url_for()生成URL,因为这个函数使用URL映射生成URL,从而保证URL和定义的路由兼容,而修改路由的名字后依然可以使用
使用session.get('name')获取字典中键对应的值以避免未找到键的异常情况,对于不存在的键,get()会返回默认值None
from flask import Flask,request,make_response,redirect,render_template,session,url_for @app.route('/', methods=['GET', 'POST']) def index(): form = NameForm() if form.validate_on_submit(): session['name'] = form.name.data return redirect(url_for('index')) return render_template('index.html', form=form, name=session.get('name'))
提交名字后再次刷新,没有告警信息
**flash消息**
请求完成,可以通过确认消息,告警或错误提示,告诉用户状态的变化
当用户提交了一项有错误的登录表单后,服务器发回的响应重新渲染了登录表单,并在表单上显示一个消息,提示用户名或密码错误
1、修改hello.py,实现flash
@app.route('/', methods=['GET', 'POST']) def index(): form = NameForm() if form.validate_on_submit(): old_name = session.get('name') if old_name is not None and old_name != form.name.data: flash('Looks like you have changed your name!') session['name'] = form.name.data return redirect(url_for('index')) return render_template('index.html', form = form, name = session.get('name'))
2、修改templates/base.html,渲染Flash消息
Flask把get_flashed_messages()函数开放给模板,用来获取并渲染消息
在模板中使用循环是因为在之前的请求循环中每次调用 flash() 函数时都会生成一个消息,
所以可能有多个消息在排队等待显示。 get_flashed_messages() 函数获取的消息在下次调
用时不会再次返回,因此 Flash 消息只显示一次,然后就消失了
{% block content %} <div class="container"> {% for message in get_flashed_messages() %} <div class="alert alert-warning"> <button type="button" class="close" data-dismiss="alert">×</button> {{ message }} </div> {% endfor %} {% block page_content %}{% endblock %} </div> {% endblock %}
3、页面中当两次提交的名字不一样时,会出现告警信息