1、WTForms介绍和基本使用
1.1 WTForm介绍
这个插件库主要有两个作用。
第一个是做表单验证,将用户提交上来的数据进行验证是否符合系统要求。
第二个是做模版渲染。 (了解即可)
官网:https://wtforms.readthedocs.io/en/latest/index.html
Flask-WTF是简化了WTForms操作的一个第三方库。WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。而Flask-WTF还包括一些其他的功能:CSRF保护,文件上传等。
安装Flask-WTF默认也会安装WTForms,因此使用以下命令来安装Flask-WTF和WTForms:
pip install flask-wtf
1.2 WTForms表单验证的基本使用
- 自定义一个表单类,继承自wtforms.Form类。
- 定义好需要验证的字段,字段的名字必须和模版中那些需要验证的input标签的name属性值保持一致。
- 在需要验证的字段上,需要指定好具体的数据类型。
- 在相关的字段上,指定验证器。
- 以后在视图函数中,只需要使用这个表单类的对象,并且把需要验证的数据,也就是request.form传给这个表单类,再调用表单类对象.validate()方法进行,如果返回True,那么代表用户输入的数据都是符合格式要求的,Flase则代表用户输入的数据是有问题的。如果验证失败了,那么可以通过表单类对象.errors来获取具体的错误信息。
示例代码:
main.py
from flask import Flask, render_template, request
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo
app = Flask(__name__)
@app.route('/')
def index():
return 'hello world'
class RegisterForm(Form):
uname = StringField(validators=[Length(min=2, max=10, message='用户名长度2-10之间')])
pwd = StringField(validators=[Length(min=2, max=10)])
pwd2 = StringField(validators=[Length(min=2, max=10), EqualTo('pwd', message='2次密码不一致')])
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
form = RegisterForm(request.form)
if form.validate(): # 验证成功:True, 失败:False
return '验证成功'
else:
return f'验证失败!{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册</title>
</head>
<body>
<form action="/register/" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="uname" id=""></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="pwd" id=""></td>
</tr>
<tr>
<td>确认密码:</td>
<td><input type="password" name="pwd2" id=""></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>
运行结果:
2、WTForm常用验证器
页面把数据提交上来,需要经过表单验证,进而需要借助验证器来进行验证,以下是常用的内置验证器。
2.1 Length
字符串长度限制,有min和max两个值进行限制,message用于提示信息。
username = StringField(validators=[Length(min=3,max=10,message="用户名长度必须在3到10位之间")])
2.2 EqualTo
验证数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。
password_repeat = StringField(validators=[Length(min=6,max=10),EqualTo("password")])
2.3 Email
验证上传的数据是否为邮箱数据格式 如:123@qq.com。
email = StringField(validators=[Email()])
2.4 InputRequired
验证该项数据为必填项,即要求该项非空。
username = StringField(validators=[input_required()])
2.5 NumberRange
数值的区间,有min和max两个值限制,如果处在这两个数字之间则满足。
age = IntegerField(validators=[NumberRange(12,18)])
2.6 Regexp
定义正则表达式进行验证,如验证手机号码。
phone = StringField(validators=[Regexp(r'1[34578]\d{9}')])
2.7 URL
必须是URL的形式 如http://www.baicu.com。
home_page = StringField(validators=[URL()])
2.8 UUID
验证数据是UUID类型。
uuid = StringField(validators=[UUID()])
2.9 实例演示
示例代码:
main.py
from flask import Flask, render_template, request
from formcheck import RegisterForm
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello! '
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
form = RegisterForm(request.form)
if form.validate():
return '验证成功!'
else:
return f'验证失败!{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
formcheck.py
from wtforms import Form, StringField, IntegerField
from wtforms.validators import Email, InputRequired, NumberRange, Regexp, URL, UUID
class RegisterForm(Form):
# pip install wtforms[email]
email = StringField(validators=[Email()])
uname = StringField(validators=[InputRequired()])
age = IntegerField(validators=[NumberRange(min=18, max=50)])
phone = StringField(validators=[Regexp('^1[3456789]\d{9}$')])
homepage = StringField(validators=[URL()])
uuid = StringField(validators=[UUID()])
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/register/" method="post">
<table>
<tr>
<th>邮箱:</th>
<td><input type="email" name="email"></td>
</tr>
<tr>
<th>用户名:</th>
<td><input type="text" name="uname"></td>
</tr>
<tr>
<th>年龄:</th>
<td><input type="number" name="age"></td>
</tr>
<tr>
<th>手机号码:</th>
<td><input type="text" name="phone"></td>
</tr>
<tr>
<th>个人主页:</th>
<td><input type="text" name="homepage"></td>
</tr>
<tr>
<th>uuid:</th>
<td><input type="text" name="uuid"></td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>
运行结果:
3、WTForms自定义验证器
只有当WTForms内置的验证器不够使的时候,才需要使用自定义验证器。
如果想要对表单中的某个字段进行更细化的验证,那么可以针对这个字段进行单独的验证。
自定义验证器步骤如下:
- 定义一个方法,方法的名字规则是: validate_字段名(self,field) 。
- 在方法中,使用 field.data 可以获取到这个字段的具体的值。
- 验证时,如果数据满足条件,那么可以什么都不做。如果验证失败,那么应该抛出一个 wtforms.validators.ValidationError 的异常,并且把验证失败的信息传到这个异常类中。
示例代码:
main.py
from flask import Flask, render_template, request, session
from formcheck import LoginForm
from random import randint
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(32)
@app.route('/')
def index():
return 'Hello! '
@app.route('/login/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
code = randint(1000, 9999)
session['code'] = code
return render_template('login.html', code=code)
else:
form = LoginForm(request.form)
if form.validate():
return '验证成功!'
else:
return f'验证失败!{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
formcheck.py
from wtforms import Form, StringField
from wtforms.validators import Length, ValidationError
from flask import session
class LoginForm(Form):
code = StringField(validators=[Length(4, 4)]) # 不能满足数据内容的匹配
def validate_code(self, field):
font_code = field.data
server_code = str(session.get('code'))
print(f'前端的数据是:{font_code} ==== 服务器端的数据是:{server_code}')
print(f'前端的数据是:{type(font_code)} ==== 服务器端的数据是:{type(server_code)}')
if font_code != server_code:
raise ValidationError('验证码错误')
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/login/" method="post">
<table>
<tr>
<th>用户名:</th>
<td><input type="text" name="uname"></td>
</tr>
<tr>
<th>密码:</th>
<td><input type="password" name="pwd"></td>
</tr>
<tr>
<th>验证码:</th>
<td><input type="text" name="code" maxlength="4"><span style="background-color: red;">{{code}}</span></td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
运行结果:
4、WTForms渲染模板
渲染模版是WTForms的第二个作用。
示例代码:
main.py
from flask import Flask, render_template, request
from formcheck import LoginForm
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello! '
@app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('login.html', form=form)
else:
return '登录成功'
if __name__ == '__main__':
app.run(debug=True)
formcheck.py
from wtforms import Form, StringField, IntegerField, BooleanField, SelectField
from wtforms.validators import Length, InputRequired, NumberRange
class LoginForm(Form):
uname = StringField(validators=[Length(min=2, max=10)])
# uname = StringField('用户名:',validators=[Length(min=1,max=10)])
age = IntegerField('年龄:', validators=[InputRequired(), NumberRange(min=15, max=25)])
remember = BooleanField('记住我:')
address = SelectField('地址:', choices=[('bj', '北京'), ('sh', '上海'), ('gz', '广州')])
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
.hot{
background-color: cadetblue;
}
</style>
</head>
<body>
<form action="/login/" method="post">
<table>
<tr>
<td>{{ form.uname.label}}</td>
<td>{{ form.uname(class='hot')}}</td>
</tr>
<tr>
<td> {{ form.age.label}}</td>
<td>{{ form.age() }}</td>
</tr>
<tr>
<td> {{ form.remember.label}}</td>
<td>{{ form.remember()}}</td>
</tr>
<tr>
<td>{{ form.address.label}}</td>
<td>{{ form.address()}}</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
运行结果:
问题遗留:
上面代码执行中,WTforms渲染模板验证没有生效!
5、Flask-wtf验证上传的文件
关键点:
- 定义验证表单类的时候,对文件类型的字段,需要采用 FileField 这个类型,即wtforms.FileField
- 验证器需要从 flask_wtf.file 中导入。 flask_wtf.file.FileRequired 和 flask_wtf.file.FileAllowed
- flask_wtf.file.FileRequired 是用来验证文件上传不能为空。
- flask_wtf.file.FileAllowed 用来验证上传的文件的后缀名, 如常见图片后缀 .jpg 和.png以及.gif等。
- 在视图函数中,需要使用 from werkzeug.datastructures import CombinedMultiDict 来把request.form 与 request.files 来进行合并。
- 最后使用 表单验证对象.validate()进行验证。
示例代码:
main.py
from flask import Flask, render_template, request
from werkzeug.datastructures import CombinedMultiDict
from werkzeug.utils import secure_filename
import os
from formcheck import UpLoadForm
app = Flask(__name__)
UPLOAD_PATH = os.path.join(os.path.dirname(__file__), 'images')
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
return render_template('upload.html')
else:
form = UpLoadForm(CombinedMultiDict([request.form, request.files]))
if form.validate():
img_file = form.pic.data
file_name = secure_filename(img_file.filename)
img_file.save(os.path.join(UPLOAD_PATH, file_name))
return '上传文件成功!'
else:
return f'{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
formcheck.py
from wtforms import Form, FileField
from flask_wtf.file import FileAllowed, FileRequired
class UpLoadForm(Form):
pic = FileField(validators=[FileRequired(), FileAllowed(['jpg', 'png', 'gif'])])
upload.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/upload/" method="post" enctype="multipart/form-data">
上传文件:<input type="file" name="pic"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
运行结果: