2.用Flask-WTF处理表单

基本处理

之前提到的请求上下文中的request.form可以获得POST请求中提交表单数据,一个个去处理显然很繁琐,这时可以用Flask-WTF来让一切变得更加简单,它集成了WTForms,安装如下:
pip install flask-wtf

设置密钥

为了防止恶意网站的CSRF攻击,Flask-WTF用一个密钥生成加密令牌,再用令牌验证数据的真伪,我们把这个密钥放在app.config字典中,这里面存储了一些框架的配置变量,还支持导入配置值(一般密钥是保存在环境变量里的),代码如下:

# demo.py
...
app = Flask(__name__)
app.config['SECRET_KEY'] = 'some secret string' 
...

使用表单类

在这个拓展中每个表单都由一个继承Form的类,里面的每个字段都定义为类变量,还可以附属验证函数,如下:

from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required

class NameForm(Form):
    name = StringField('你的名字:', validators=[Required()]) # validator为验证函数组成的列表
    submit = SubmitField('Go')

WTForms支持的HTML标准字段

字段类型 说 明
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密码文本字段
HiddenField 隐藏文本字段
DateField 文本字段,值为datetime.date格式
DateTimeField 文本字段,值为datetime.datetime格式
IntegerField 文本字段,值为整数
DecimalField 文本字段,值为decimal.Decimal
FloatFiled 文本字段,值为浮点数
BooleanField 复选框,值为True和False
RadioField 一组单选框
SelectField 下拉列表
SelectMultipleField 下拉列表,可选择多个值
FileField 文件上传字段
SubmitField 表单提交按钮
FormField 把表单作为字段嵌入另一表单
FieldList 一组指定类型的字段

WTForms验证函数:

验证函数 说 明
Email 验证电子邮件地址
EqualTo 比较两个字段的值,常用于要求输入两次密码确认的情况
IPAddress 验证IPv4网络地址
Length 验证输入字符串的长度
NumberRange 验证输入的值在数字范围内
Optional 无输入值时跳过其他验证函数
Required 确保字段中有数据
Regexp 使用正则表达式验证输入值
URL 验证URL
AngOf 确保输入值在可选值列表中
NoneOf 确保输入值之不在可选值列表中

页面部分:渲染表单

接下来要在页面上显示,我们可以把表单实例通过参数form传入模板,如下:

<form method="POST">
    {
     { form.hidden_tag() }}
    {
     { form.name.label }} {
     { form.name() }}
    {
     { form.submit() }}
form>

有些麻烦是吧,好在Flask-Bootstrap提供了一个好用的函数quick_form(),它接收一个表单对象,方便快速的渲染表单:

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %} 

{% block title %}首屋{% endblock %}
{% block page_content %}
<div class="page-header">
   <h1>你来了, {% if name %} {
     { name }}{% else %}外乡人{% endif %}!!h1>
div>
{
     { wtf.quick_form(form) }} 
{% endblock %}

运行如图:
2.用Flask-WTF处理表单_第1张图片

后端部分:处理表单

接收到POST请求后要进行处理,由于之前的首页路由只有默认的GET请求方法,我们要添加POST方法,虽然表单也可以用GET提交(以查询字符串形式加到URL中),但它没有主体以及其他安全问题,还是稳稳用POST吧

改写的index路由如下:

@app.route('/', methods=['GET', 'POST'])  # methods参数注册请求方法
def index():
    name = None
    form = NameForm()  # 实例化一个表单
    if form.validate_on_submit():  # 表单成功提交后进入
        name = form.name.data    # 通过字段的data属性获取
        form.name.data = '' # 获取数据后清空表单
    return render_template('index.html', form=form, name=name, cur_time=datetime.utcnow(), time=datetime(1995,7,1))

这个过程:第一次GET获取页面和表单,第二次POST提交表单进入if获得参数进行渲染

提交表单后:
2.用Flask-WTF处理表单_第2张图片

用户会话(session)的使用

它是之前提到过的请求上下文中的变量(另一个是request),session以字典的形式存储请求之间的数据,存于客户端的cookie中并用使SECRET_KEY加密,为什么要用它,需要了解一下重定向

重定向是一种特殊响应,响应内容是URL,浏览器接收到这种响应时会向重定向的URL发起GET请求来显示页面的内容

因为刷新页面时浏览器会重新发送之前发送的最后一个请求,如果是POST(包含表单数据)那刷新后就会再次提交,显然这是不好的,不要把POST请求作为浏览器的最后一个请求,这时可以用重定向来实现,这种避免POST放在最后而用GET的技巧被称作 Post/重定向/Get模式,但当一个请求结束时数据也会跟着丢失,此时session就派上了用场:

from flask import Flask, render_template, redirect, url_for, session  # 记得导入所用的
@app.route('/', methods=['GET', 'POST'])  # methods参数注册请求方法
def index():
    form = NameForm()  # 实例化一个表单
    if form.validate_on_submit():  # 判断表单是否成功提交
        session['name'] = form.name.data   # 把值保存在session中
        return redirect(url_for('index'))  # 为了兼容
    return render_template('index.html', form=form, name=session.get('name'), cur_time=datetime.utcnow(), time=datetime(1995,7,1))

url_for()用于生成URL,它只有一个参数,即端点名(在这里视图函数名),用这个函数可以防止修改路由后URL不可用的问题;session.get('name')很灵活,当值未找到时返回None,因此省去了name=None

提交表单后的请求:
2.用Flask-WTF处理表单_第3张图片

Flash提醒

当请求完成后想有点反馈,告知客户端成功或失败,flask有个flash()函数就是专门干这事的,先看后端代码:

from flask import flash  # 记得导入
@app.route('/', methods=['GET', 'POST'])  # methods参数注册请求方法
def index():
    form = NameForm()  
    if form.validate_on_submit():  
        oldname = session.get('name')  
        if oldname is not None and oldname != form.name.data: # 提交的名字与之前的名字比较
            flash("我们不一样")
        session['name'] = form.name.data
        return redirect(url_for('index'))
    return render_template('index.html', form=form, name=session.get('name'), cur_time=datetime.utcnow(), time=datetime(1995,7,1))

然后在模板中渲染,Flask把get_flashed_messages()函数开放给了模板,放在content块中:

{% 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 %}    

输入两次不同后如下:
2.用Flask-WTF处理表单_第4张图片
在之前的请求循环中每次调用flash()函数都会生成一个消息,会形式多个消息排队等待的情况,所以用for;get_flashed_messages()获取的消息在下次调用时不会再次返回,所以Flash消息只显示一次

你可能感兴趣的:(Flask-Web,web表单,wtf,flask-wtf)