flask插件之flask-wtf,处理web表单

1. flask-wtf

flask-wtf 是专门用来处理web表单的插件,它可以减少你在前端的工作量,简化你在后端处理post表单请求时的工作,能够自动对前端提交的form数据做验证。

安装方法如下

pip install flask-WTF

本教程编写时使用的版本是flask-WTF-1.0.1

2. 一个简单的登录例子

2.1 定义你的Form

使用flask-wtf ,需要你根据表单定义Form类, 我以登录为例

class LoginForm(FlaskForm):
    username = StringField('username')
    password = PasswordField('password')

2.2 前端页面设计

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="/login", method="post">
        {{ form.csrf_token }}
        {{ form.username.label }} {{ form.username(size=20) }}
        {{ form.password.label }} {{ form.password(size=20) }}
        <input type="submit" value="登录">
    </form>
</body>
</html>

在form表单里,我并没有编写类似下面的代码

<label for="username">username</label> <input id="username" name="username" size="20" type="text" value="">

这样的代码,flask在渲染时会根据我所定义的LoginForm 自动生成,这样就减少了前端的一部分工作量,对于专业的前端来说,他可能更喜欢自己写标签,但如果你是全栈工程师,wtf还是能帮到你的。

在编写一个首页,index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
  <p> 欢迎你 {{ username }}</p>
</body>
</html>

用户登录成功以后,将显示登录者的username

2.3 使用flask-wtf 处理post 请求

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        if form.password.data == "123456":
            response = redirect('/index')
            response.set_cookie('username', form.username.data )
            return response

    return render_template("login.html", form=form)

validate_on_submit 会判断请求是否是POST方法,以及提交的表单是否正确,验证逻辑,我只比较password是否等于123456, 然后将username 设置到cookie里,随后跳转到首页

2.4 后端完整代码

from flask import Flask, render_template, request, redirect
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField


class LoginForm(FlaskForm):
    username = StringField('username')
    password = PasswordField('password')

app = Flask(__name__)
app.secret_key = "hard to guess"

@app.route('/index')
def index():
    username = request.cookies.get("username", '')
    return render_template('index.html', username=username)


@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        if form.password.data == "123456":
            response = redirect('/index')
            response.set_cookie('username', form.username.data )
            return response

    return render_template("login.html", form=form)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5500)

在前端页面的表单里,我添加了csrf_token, 这是为了防止跨站请求伪造。

3. validators 验证表单数据

在第二节的例子里,并没有对post所提交的表单进行字段正确性检查,flask-wtf 允许你为一个字段指定多个validator进行检查。

常用的验证器有:

  1. DataRequired ,检查属性值是否为True, 若字段是字符串类型,为空字符串时等价于False
  2. EqualTo 判断是否与另一个字段相等
  3. Email 验证是否是email
  4. InputRequired 验证是否有输入
  5. Regexp 验证是否符合正则要求

除了模块自身提供的验证器,还可以自定义验证器

from flask import Flask, render_template, request, redirect, flash
from flask_wtf import FlaskForm
from wtforms.validators import InputRequired, ValidationError
from wtforms import StringField, PasswordField

def password_check(form, field):
    if len(field.data) != 6:
        raise ValidationError('密码必须是6位')

class LoginForm(FlaskForm):
    username = StringField('username', validators=[InputRequired()])
    password = PasswordField('password', validators=[InputRequired(), password_check])

自定义验证器必须接收两个参数,分别是form和field, 验证器不通过时,可以通过form.errors 来获取不通过的原因

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        if form.password.data == "123456":
            response = redirect('/index')
            response.set_cookie('username', form.username.data )
            return response
    else:
        if request.method == "POST":
            errors = form.errors        # 表单验证不通过的原因
            print(errors)
            flash(str(errors))

    return render_template("login.html", form=form)

验证错误,我简单的通过flash传递给前端页面,前端页面也稍作修改, login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    {% with messages = get_flashed_messages() %}
      {% if messages %}
        <ul class=flashes>
        {% for message in messages %}
          <li>{{ message }}</li>
        {% endfor %}
        </ul>
      {% endif %}
    {% endwith %}
</head>
<body>
    <form action="/login", method="post">
        {{ form.csrf_token }}
        {{ form.username.label }} {{ form.username(size=20) }}
        {{ form.password.label }} {{ form.password(size=20) }}
        <input type="submit" value="登录">
    </form>
</body>
</html>

启动服务进入登录页面,这一次,某个字段如果没有填写,前端页面就会提示你必须填写,如果密码长度不符合要求,会在前端页面里显示出错误原因。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案