表单可以提供文本输入框、单选按钮、复选框、按钮等元素供用户提交数据。在Flask项目中,表单除了可以表示传统的HTML标签,还可以验证数据。数据被发送到服务器后,为了防止不法分子绕过前端限制提交非法数据,需要对数据验证。为实现验证功能,需要Flask-WTF插件,使用pip install flask-wtf安装。
以注册功能为例,验证表单验证功能。首先在templates文件夹中创建register.html文件。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册title>
head>
<body>
<form action="{{ url_for('register') }}" method="POST">
<table>
<tr>
<td>用户名:td>
<td><input type="text" name="username">td>
tr>
<tr>
<td>邮箱:td>
<td><input type="email" name="email">td>
tr>
<tr>
<td>密码:td>
<td><input type="password" name="password">td>
tr>
<tr>
<td>td>
<td><input type="submit" value="提交">td>
tr>
table>
form>
body>
html>
模板写好后,使用视图函数渲染。
@app.route("/register", methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
pass
打开https://127.0.0.1:5000/register后,即可看到模板被渲染后的结果:


用户可以在页面中输入信息,但是每个输入的信息字段都有一定的规则要求,如果用户的输入有误,应该在界面中给予及时的提示。这个工作可以在前端由JavaScript完成,但是服务器端也应该做好验证,因为对于有一定技术功底的用户来说,可以通过抓包的形式获取注册时的请求数据,然后通过代码或工具模拟注册,这完全绕开了JavaScript验证。
服务器端的验证也可以通过WTForms来实现,首先在项目的根路径下创建forms.py。
forms.py
from wtforms import Form, StringField
from wtforms.validators import length, email, equal_to
class RegisterForm(Form): #所有表单类都必须继承自Form基类
username = StringField(validators=[length(min=3, max=20, message="请输入正确长度的用户名,不少于3位而不多于20位。")])
email = StringField(validators=[email(message="请输入正确格式的邮箱!")]) #验证内容是否满足邮箱的格式规则
password = StringField(validators=[length(min=60, max=20, message="请输入正确长度的密码!")])
confirm_password = StringField(validators=[equal_to("password",message="两次密码长度不一致!")])
#👆字段的名称必须和HTML中模板中表单元素的name一致。
#StringField指的是字符串类型,此外还有IntegerField、FloatField等
以下以完善register视图函数为例:完善了register视图函数的POST功能,但是仍然不完整。
@app.route("/register", methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
#request.form时html模板提交上来的表单数据
form = RegisterForm(request.form) #需要使用import从刚刚编写的forms.py导入RegisterForm
#如果表单验证通过
if form.validate():
#👇通过form.<字段名>.data获取字段对应的数据
email = form.email.data
username = form.username.data
password = form.username.data
#以下代替把数据保存到数据库中的操作
print("email:", email)
print("username:", username)
print("password:", password)
return "注册成功!"
else:
for errors in form.errors.values():
for error in errors:
flash(error)
return redirect(url_for("register"))
WTForms和Flask-WTF都提供了将Python表单对象渲染成HTML表单模板的功能。这里以登录为例,先来实现一个登录的表单类。
from wtforms import Form, StringField, BooleanField, SubmitField, ValidationError
from wtforms.validators import length, email, equal_to
from flask_wtf import FlaskForm
#👇定义一个LoginForm,并使之继承自FlaskForm,FlaskForm的父类是wtforms.Form类
class LoginForm(FlaskForm):
email = StringField(label="邮箱", validators=[email(message="请输入正确格式的邮箱!")],render_kw={
"placeholder":"请输入邮箱"
})
password = StringField(label="密码", validators=[length(min=6, max=20, message="请输入正确长度的密码!")],
render_kw={"placeholder":"请输入密码"})
remember = BooleanField(label="记住我:")
submit = SubmitField(label="提交")
表单类定义好后,就可以在视图函数中使用了。
@app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm(meta={"csrf":False})
if form.validate_on_submit():
email = form.email.data
password = form.password.data
return redirect('/') #如果提交成功,重定向至‘/’
return render_template("login.html",form=form)
👆如果登录成功,重定向至根路径https://127.0.0.1:5000。接下来可以定义login.html的模板了。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="" method="POST">
<table>
<tbody>
<tr>
<td>{{ form.email.label }}td>
<td>{{ form.email }}td>
tr> #如果有错误,在输入框下显示
{% for error in form.email.errors %}
<tr>
<td>td>
<td>{{ error }}td>
tr>
{% endfor %}
<tr>
<td>{{ form.password.label }}td>
<td>{{ form.password }}td>
tr> #如果有错误,在输入框下显示
{% for error in form.password.errors %}
<tr>
<td>td>
<td>{{ error }}td>
tr>
{% endfor %}
<tr>
<td>{{ form.remember.label }}td>
<td>{{ form.remember() }}td>
tr>
<tr>
<td>td>
<td>{{ form.submit }}td>
tr>
tbody>
table>
form>
body>
html>

通过SCRFProtect可以开启全局防御。
app = Flask(__name__)
app.secret_key = "自定的app密钥"
CSRFProtect(app)
👆在app.py中加入上述代码
再在register.html模板中加入以下代码:
<body>
<form action="{{ url_for('register') }}" method="POST">
<table>
<tr>
<td>td>
<td><input type="hidden" name="csrf_token" value="{{ IjQ0 }}">td>
tr>
......
此时再次运行app.py,直接点击提交会出现以下信息:

当然,书中的csrf_token比此处的复杂得多。