第四章 表单 (2)

处理表单数据

表单数据的处理涉及很多内容,除去表单提交不说,从获取数据到保存数据大致会经历以下步骤:
1)解析请求,获取表单数据。
2)对数据进行必要的转换,比如将勾选框的值转换成Python的布尔值。
3)验证数据是否符合要求,同时验证CSRF令牌。
4)如果验证未通过则需要生成错误消息,并在模板中显示错误消息。
5)如果通过验证,就把数据保存到数据库或做进一步处理。
除非是简单的程序,否则手动处理不太现实,使用Flask-WTF和WTForms可以极大地简化这些步骤。 

提交表单 

在HTML中,当<form>标签声明的表单中类型为submit的提交字段被单击时,就会创建一个提交表单的HTTP请求,请求中包含表单各个字段的数据。表单的提交行为主要由三个属性控制 .

form标签的action属性用来指定表单被提交的目标URL,默认为当前URL,也就是渲染该模板的路由所在的URL。如果你要把表单数据发送到其他URL,可以自定义这个属性值。
当使用GET方法提交表单数据时,表单的数据会以查询字符串的形式附加在请求的URL里,比如:

http://localhost:5000/basic?username=greyli&password=12345

 GET方式仅适用于长度不超过3000个字符,且不包含敏感信息的表单。因为这种方式会直接将用户提交的表单数据暴露在URL中,容易被攻击者截获,示例中的情况明显是危险的。因此,出于安全的考虑,我们一般使用POST方法提交表单。使用POST方法时,按照默认的编码类型,表单数据会被存储在请求主体中,比如:

POST /basic HTTP/1.0
...
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
username=greyli&password=12345

为了支持接收表单提交发送的POST请求,我们必须在app.route()装饰器里使用methods关键字为路由指定HTTP方法

@app.route('/', methods=['GET', 'POST'])
def basic():
    form = LoginForm()
    return render_template('basic.html', form=form)

 

验证表单数据

1.客户端验证和服务器端验证

(1)客户端验证

在客户端(比如Web浏览器)对用户的输入值进行验证,使用HTML5内置的验证属性即可实现基本的客户端验证(type、required、min、max、accept等)。比如,下面的username字段添加了required标志:

<input type="text" name="username" required>

如果用户没有输入内容而按下提交按钮,会弹出浏览器内置的错误提。

我们可以在定义表单时通过render_kw传入这些属性,或是在渲染表单时传入。像required这类布尔值属性,值可以为空或是任意ASCII字符,比如:

{{ form.username(required='') }}

除了使用HTML5提供的属性实现基本的客户端验证,我们通常会使用JavaScript实现完善的验证机制,可以考虑使用各种JavaScript表单验证库,比如jQuery Validation
Plugin等。

客户端方式可以实时动态提示用户输入是否正确,只有用户输入正确后才会将表单数据发送到服务器。客户端验证可以增强用户体验,降低服务器负载。

 

(2)服务器端验证

服务器端验证(server side validation)是指用户把输入的数据提交到服务器端,在服务器端对数据进行验证。如果验证出错,就在返回的响应中加入错误信息。用户修改后再次提交表单,直到通过验证。我们在Flask程序中使用WTForms实现的就是服务器端验证。

 

2.WTForms验证机制

WTForms验证表单字段的方式是在实例化表单类时传入表单数据,然后对表单实例调用validate()方法。这会逐个对字段调用字段实例化时定义的验证器,返回表示验证结果的布尔值。如果验证失败,就把错误消息存储到表单实例的errors属性对应的字典中,验证的过程如下所示:

>>> from wtforms import Form, StringField, PasswordField, BooleanField
>>> from wtforms.validators import DataRequired, Length
>>> class LoginForm(Form):
... username = StringField('Username', validators=[DataRequired()])
... password = PasswordField('Password', validators=[DataRequired()
, Length(8, 128)])
>>> form = LoginForm(username='', password='123')
>>> form.data # 表单数据字典
{'username': '', 'password': '123'}
>>> form.validate()
False
>>> form.errors # 错误消息字典
{'username': [u'This field is required.'], 'password': [u'Field must be
at least 6 characters long.']}
>>> form2 = LoginForm(username='greyli', password='123456')
>>> form2.data
{'username': 'greyli', 'password': '123456'}
>>> form2.validate()
True
>>> form2.errors
{}

使用POST方法提交的表单,其数据会被Flask解析为一个字典,可以通过请求对象的form属性获取(request.form);使用GET方法提交的表单的数据同样会被解析为字典,不过要通过请求对象的args属性获取(request.args)。

3.在视图函数中验证表单

因为现在的basic_form视图同时接收两种类型的请求:GET请求和POST请求。所以我们要根据请求方法的不同执行不同的代码。具体来说:首先是实例化表单,如果是GET请求,那么就渲染模板;如果是POST请求,就调用validate()方法验证表单数据。

请求的HTTP方法可以通过request.method属性获取

from flask import request
...
@app.route('/basic', methods=['GET', 'POST'])
def basic():
    form = LoginForm() # GET + POST
    if request.method == 'POST' and form.validate():
        ... # 处理POST请求
    return render_template('forms/basic.html', form=form) # 处理GET请求

Flask-WTF提供的validate_on_submit()方法合并了这两个操作,因此上面的代码可以简化为:

@app.route('/basic', methods=['GET', 'POST'])
def basic():
    form = LoginForm()
    if form.validate_on_submit():
        ...
    return render_template('basic.html', form=form)

除了POST方法,如果请求的方法是PUT、PATCH和DELETE方法,form.validate_on_submit()也会验证表单数据。

 

表单类的data属性是一个匹配所有字段与对应数据的字典,我们一般直接通过“form.字段属性名.data”的形式来获取对应字段的数据。例如,form.username.data返回username字段的值。当表单验证成功后,我们获取了username字段的数据,然后用来发送一条flash消息,最后将程序重定向到index视图。

from flask import Flask, render_template, redirect, url_for, flash
...
@app.route('/basic', methods=['GET', 'POST'])
def basic():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        flash('Welcome home, %s!' % username)
        return redirect(url_for('index'))
    return render_template('basic.html', form=form)

 

在模板中渲染错误消息

如果form.validate_on_submit()返回False,我们一般会直接通过字段名来获取对应字段的错误消息列表,即“form.字段名.errors”。比如,form.name.errors返回name字段的错误消息列表。

<form method="post">
    {{ form.csrf_token }}
    {{ form.username.label }}<br>
    {{ form.username() }}<br>
    {% for message in form.username.errors %}
        <small class="error">{{ message }}</small><br>
    {% endfor %}
    {{ form.password.label }}<br>
    {{ form.password }}<br>
    {% for message in form.password.errors %}
        <small class="error">{{ message }}</small><br>
    {% endfor %}
    {{ form.remember }}{{ form.remember.label }}<br>
    {{ form.submit }}<br>
</form>

在使用DataRequired和InputRequired验证器时,WTForms会在字段输出的HTML代码中添加required属性,所以会弹出浏览器内置的错误提示。同时,WTForms也会在表单字段的flags属性添加required标志(比如form.username.flags.required),所以我们可以在模板中通过这个标志值来判断是否在字段文本中添加一个*号或文字标注,以表示必填项。

InputRequired仅验证用户是否有输入,而不管输入的值是否有效。例如,由空格组成的数据也会通过验证。当使用DataRequired时,如果用户输入的数据不符合字段要求,比如在
IntegerField输入非数字时会视为未输入,而不是类型错误。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值