4

Flask表单:表单数据的验证与处理

 3 years ago
source link: https://greyli.com/flask-form-form-data-validation-and-processing/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这篇文章作为上一篇的续篇,所以结构上也和上一篇一样。

使用Flask-WTF

这是一个登录的视图函数:

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        email = form.email.data
    return render_template('login.html', form=form)

验证数据

当我们点击了表单上的提交按钮时,form.validate_on_submit()判断会做下面两件事情:

  1. 通过is_submitted()通过判断HTTP方法来确认是否提交了表单
  2. 通过WTForms提供的validate()来验证表单数据(使用我们在下面的表单类里给每个字段传入的验证函数)

这是我们的表单类:

class LoginForm(FlaskForm):
    email = StringField(u'邮箱', validators=[
                DataRequired(message= u'邮箱不能为空'), Length(1, 64),
                Email(message= u'请输入有效的邮箱地址,比如:[email protected]')])
    password = PasswordField(u'密码',
                  validators=[Required(message= u'密码不能为空')])
    submit = SubmitField(u'登录')

在Flask-WTF 0.13版本,引入的表单类为FlaskForm
在WTForms 3.0版本,验证函数Required变为DataRequired

当validate()验证未通过时,会在表单字段下面显示我们传进去的错误提示(例如message= u’邮箱不能为空’)。

自定义验证

你可以在表单类创建自定义的验证函数,一个简单的例子:

def validate_username(self, field):
    if User.query.filter_by(username=field.data).first():
        raise ValidationError(u'用户名已被注册,换一个吧。')

这个例子验证用户名是否已经存在(这里使用SQLAlchemy),其中field.data是数据。

ValidationError从wtforms导入,用来向用户显示错误信息,验证函数的名称由validate_fieldname组成。你也可以在这里对用户数据进行预处理:

def validate_website(self, field):
    if field.data[:4] != "http":
        field.data = "http://" + field.data

这个函数对用户输入的网址进行处理(字段名为website)。

你也可以在表单类外面定义一个通用的验证函数,然后传入字段的验证函数列表里,具体见WTForms文档:http://wtforms.readthedocs.io/en/latest/validators.html#custom-validators

获取数据

验证通过后,我们使用form.email.data来获得数据,WTForms提供的静态方法.data返回一个以字段名(field name)和字段值(field value)作为键值对的字典。

不使用Flask-WTF,只使用WTForms

我们既然已经知道了Flask-WTF的form.validate_on_submit()的工作原理,我们也可以自己实现:

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if request.method == 'POST' and form.validate():
        email = form.email.data
    return render_template('login.html', form=form)

但是这样要复杂一点,而且在渲染时比较繁琐,如果你想了解更多,可以参考:http://flask.pocoo.org/docs/0.11/patterns/wtforms/

不使用Flask-WTF,也不使用WTForms

在一些特殊场景下,比如一个特别简单的表单。这是上一个实践项目的表单:

<form method="POST" action="{{ url_for('custom') }}">
    <input type="text" name="time" class="time-input" placeholder="example: 12/30s/20m/2h">
    <input type="submit" class="startButton" value="START">
</form>

这时要给表单添加一个action属性,属性值填写要处理表单数据的视图函数。这是处理表单数据的函数:

@app.route('/custom', methods=['GET', 'POST'])
def custom():
    if request.method == 'POST':
        time = request.form.get('time', 180)

获取数据

使用request来获取数据(request从flask导入),使用字段的name值来区分,你可以用:

request.form['input-name']
request.form.get('input-name', default_value)

注意如果你使用第一种方式获取数据,要确保有数据才行,像勾选框这样可以留空的字段,如果用户没有填写数据,提交后会引起HTTP 400错误,这时应该使用第二种方式来设置一个默认值。

你也可以像使用Flask-WTF一样为表单填入已经存储在数据库里的内容,不过要自己填到表单里:

@app.route('/custom', methods=['GET', 'POST'])
def custom():
    if request.method == 'POST':
        time = request.form.get('time', 180)
    email = '[email protected]'
    return render_template('demo.html', email=email)

在HTML里,将email作为value的值:

<input name="email" value="{{ email }}">

验证数据

因为没有使用WTForms,所以验证数据要自己实现,常见的验证通过你的Python知识大多都很容易实现,错误信息则可以使用flash()传递。

大多数新手对正则表达式比较头疼,在这里推荐一个在线正则表达式编辑网站,regex101.com,支持PHP、PCRE、Python、Golang和 JavaScript,可以写测试,查cheatsheet,生成代码,解释表达式,非常好用!

实践:多个表单字段的生成及数据的获取

其实这是后面豆瓣相册实践项目里的内容,就提前透露一点吧。在豆瓣相册里,有一个批量修改的功能,在这个页面,要为你的相册里的每一张图片生成一个编辑框。我们可以直接使用for循环在HTML里生成表单。

生成表单

用for循环生成input,注意我用每个图片的id作为相应输入框的name值:

<form action="{{ url_for('.edit_photos', id=album.id) }}" method="POST">
<ul>
    {% for photo in photos %}
    <li>
        <img class="img-responsive portrait" src="{{ photo.path }}" alt="Some description"/>
        <textarea name="{{ photo.id }}" placeholder="add some description" rows="3">{% if photo.description %}{{ photo.description }}{% endif %}</textarea>
    </li>
    {% endfor %}
</ul>
<hr>
<input class="btn btn-success" type="submit" name="submit" value="submit">

获取数据

然后我在视图函数同样用for循环迭代所有图片,然后使用request获取相应的数据(通过图片的id作为name值获取)然后保存到数据库:

@app.route('/edit-photos/<int:id>', methods=['GET', 'POST'])
@login_required
def edit_photos(id):
    album = Album.query.get_or_404(id)
    photos = album.photos.order_by(Photo.order.asc())
    if request.method == 'POST':
        for photo in photos:
            photo.about = request.form[str(photo.id)]
            db.session.add(photo)
        return redirect(url_for('.album', id=id))
    return render_template('edit_photo.html', album=album, photos=photos)

就先简单讲一下,具体内容等到后面再讲。

相关链接

– – – – –

更多关于Flask的优质内容,欢迎关注Hello, Flask! – 知乎专栏


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK