Flask学习(3)——表单

在Flask中,通常使用Flask-WTF扩展来对Web表单进行处理,这样更加方便。首先在虚拟环境中安装此扩展:

pip install flask-wtf

一、配置

Flask-WTF扩展不用在应用层初始化,但是需要配置一个密钥(SECRET_KEY),Flask 使用这个密钥保护用户会话,以防被篡改,也就是防止 CSRF 攻击。

app = Flask(_name__)
app.config['SECRET_KEY'] = 'I am Lethe'

app.config 字典可用于存储 Flask、扩展和应用自身的配置变量。实际上为了增强安全性,密钥不应该直接写入源码,而要保存在环境变量中。


二、表单类

使用Flask-WTF时,服务端的每个Web表单都由一个继承自 FlaskForm 的类表示。这个类定义表单中的一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数 。验证函数用于验证用户提交的数据是否有效。

下面的表单示例中包含一个文本字段和一个提交按钮:

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class NameForm(FlaskForm):
    name = StringField('What is your name?', validator=[DataRequired()])
    submit = SubmitField('Submit')

上面代码中:

  • StringField 类表示属性为 type="text" HTML 元素。
  • SubmitField 类表示属性为 type="submit" 的HTML元素 元素。
  • 字段构造函数的第一个参数代表使用的标注(label)
  • StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接收数据前对数据进行验证(DataRequired 确保提交的数据不为空)

WTForms 支持的 HTML 标准字段:

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

WTForms 验证函数:

验证函数 说明
DataRequired 确保转换类型后字段中有数据
Email 验证电子邮件地址
EqualTo 比较两个字段的值;常用于要求输入两次密码进行确认的情况
InputRequired 确保转换类型前字段中有数据
IPAddress 验证 IPv4 网络地址
Length 验证输入字符串的长度
MacAddress 验证 MAC 地址
NumberRange 验证输入的值在数字范围之内
Optional 允许字段中没有输入,将跳过其他验证函数
Regexp 使用正则表达式验证输入值
URL 验证 URL
UUID 验证 UUID
AnyOf 确保输入值在一组可能的值中
NoneOf 确保输入值不在一组可能的值中

三、表单渲染

表单字段是可调用的,在模板中调用后会渲染成HTML。假设视图函数通过 form 参数把上面的 NameForm 实例传入模板,在模板中可以生成一个简单的HTML表单,如下:

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

这里的 form.hidden_tag() 元素生成一个隐藏的字段,用于 CSRF 防护。

在调用字段的时候,传入的关键词参数将转换为字段的HTML属性。 如下,指定id或class属性,从而可以定义CSS样式:

<form method="POST">
    {{ form.hidden_tag() }}
    {{ form.name.label }} {{ form.name(id='my-text-field') }}
    {{ form.submit() }}
form>

在实际渲染及美化表单时,可以直接使用 Bootstrap 的表单样式,通过Flask-Bootstrap扩展,如下:

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(from) }}

使用 import 导入 bootstrap/wtf.html 文件,其中定义了一个用默认Bootstrap 样式渲染的 Flask-WTF 表单对象的辅助函数 wtf.quick_form() ,参数为Flask-WTF 表单对象。

使用Flask-WTF 和 Flask-Bootstrap渲染表单,templates/index.html:

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

四、处理表单

定义了表单的类,需要在视图函数中对表单进行处理和渲染,下例使用 GET 和 POST 方法处理表单:

hello.py

@app.route('/', methods=['GET', 'POST'])
def index():
    name = None
	form = NameForm()
	if form.validate_on_submit():
		name = form.name.data # 将表单中name字段值赋给name
		form.name.data = '' # 清空表单
	return render_template('index.html', form=form, name=name)
  • 局部变量 name 用于存放表单中的名字字段,默认值为 None。
  • validate_on_submit() 方法:提交表单后,数据能被所有验证函数接收,返回True;否则返回False。
  • 最后用 render_template() 渲染表单时,将变量name,和对象form传入到模板中。

效果如下:
Flask学习(3)——表单_第1张图片
提交Lethe后:
Flask学习(3)——表单_第2张图片

五、重定向和用户会话

在实际情况中,最好别让Web应用把 POST 请求作为浏览器发送的最后一个请求,因此可以使用重定向作为POST请求的响应。

如在用POST方式提交完表单后,再向重定向的URL发送GET请求,显示页面的内容,用户不会察觉到有所不同,这叫做 “POST / 重定向 / GET” 模式。

在这种模式下,应用处理POST请求时,需要用变量保存输入的数据,否则一旦重定向后,就无法再通过form.name.data获取POST请求中的字段值。

应用也可以把数据存储在用户会话(session)中,以便在请求之间“记住”数据。用户会话是一种私有存储,每个连接到服务器的客户端都可访问。

改进后的hello.py:

from flask import Flask, render_template, session, redirect, url_for
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = 'I am Lethe'

class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')

@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'))
  • 相比于上一个例子中,将 name保存在局部变量中,这里将其保存在了用户会话中,即 session[‘name’]。
  • 若表单数据有效,则会用 redirect() 函数进行重定向,其参数为URL,因此可以使用辅助函数 url_for() 来生成URL,这里即重定向到根URL。
  • 视图函数在向模板传参时,通过 session.get(‘name’) 从session中取出数据

六、闪现消息

请求完成后,有时需要向用户发送一些提示信息,如用户名或密码无效等,这可以用到闪现消息。

Flask中通过 flash()函数实现此功能:

from flask import Flask, render_template, session, redirect, url_for, flash
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_moment import Moment

app = Flask(__name__)
bootstrap = Bootstrap(app)
moment = Moment(app)
app.config['SECRET_KEY'] = 'I am Lethe'


class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')


@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'))
  • 当两次提交的name不一样时,就会调用 flash()函数,在发给客户端的下一个响应中显示一个消息。
  • 仅调用 flash() 函数不能将消息显示出来,还需要经过模板的渲染。最好在基模板中渲染闪现消息,这样继承自它的所有页面都能显示要闪现的消息。

Flask中用 get_flashed_message() 函数在模板中获取并渲染闪现消息,如下修改emplates/base.html中的 content 块:

{% block content %}
<div class="container">
    {% for message in get_flashed_message() %}
    <div class="alert alert-warning">
        <button type="button" class="close" data-dismiss="alert">×button>
        {{ message }}
    div>
    {% endfor %}
    {% block page_content %}{% endblock %}
div>
{% endblock %}
  • 此实例使用Bootstrap 提供的 CSS alert 样式渲染警告消息。
  • 使用了循环,因为在之前的请求循环中每次调用 flash() 函数时都会生成一个消息,所以可能有多个消息在排队等待显示。
  • get_flashed_messages() 函数获取的消息在下次调用时不会再次返回,因此闪现消息只显示一次,然后就消失了

两次提交不同的值,效果如下:

Flask学习(3)——表单_第3张图片

你可能感兴趣的:(Flask,flask,python)