pip install flask-wtf
。
一、WTForms表单验证
1.表单验证的基本使用
不使用WTF进行表单验证测试:
创建程序主入口wtf_demo.py:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')
def index():
return '首页'
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
username = request.form.get('username')
password = request.form.get('password')
pwd_confirm = request.form.get('pwd_confirm')
if len(username) 15:
return '用户名长度不正确'
if password != pwd_confirm:
return '两次密码输入不一致'
if len(username) 20:
return '密码长度不正确'
if __name__ == '__main__':
app.run(debug=True)
模板目录templates下创建register.html如下:
注册
用户名:
密码:
确认密码:
显示:
显然,可以达到验证表单的效果,但是在register()
视图函数中一般只是得到验证是否成功的结果即可,可以将验证单独抽离出来,使用flask-wtf进行专门验证,新建forms.py如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
修改wtf_demo.py如下:
from flask import Flask, request, render_template
from forms import RegisterForm
app = Flask(__name__)
@app.route('/')
def index():
return '首页'
@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 '验证失败:\n' + str(form.errors)
if __name__ == '__main__':
app.run(debug=True)
RegisterForm初始化时传入request.form,并且根据form.validate()
的值来判断用户提交的数据是否满足表单的验证。
显示:
在forms.py中指定了需要上传的参数,并且指定了验证器,比如username的长度应该在3-15之间,password长度必须在6-20之间,并且应该和pwd_confirm相等才能通过验证。
可以直接使用Email类进行右键地址的验证。
在forms.py中新增LoginForm如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, Email
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
email = StringField(validators=[Email(message='邮箱格式不正确')])
主程序中增加视图函数:
from flask import Flask, request, render_template
from forms import RegisterForm, LoginForm
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@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 '验证失败:\n' + str(form.errors)
@app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
form = LoginForm(request.form)
if form.validate():
return '验证成功'
else:
return '验证失败:\n' + str(form.errors)
if __name__ == '__main__':
app.run(debug=True)
新建login.html如下:
登录
邮箱:
运行主程序,显示:
可以通过NumberRange类对数的范围进行验证。
修改forms.py如下;
from wtforms import Form, StringField, IntegerField
from wtforms.validators import Length, EqualTo, Email, NumberRange
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
age = IntegerField(validators=[NumberRange(1, 120, message='年龄范围有误!!!')])
修改login.html如下:
登录
年龄:
显示:
可以通过InputRequired类限制某些字段必填。
forms.py修改如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, InputRequired
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
username = StringField(validators=[InputRequired(message='用户名必填')])
login.html修改如下:
登录
用户名:
显示:
可以通过Regexp类自定义正则表达式进行验证。
forms.py修改如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, Regexp
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
phone_number = StringField(validators=[Regexp(r'1[35789]\d{9}', message='手机号码不正确')])
login.py修改如下:
登录
手机号:
显示:
可以使用URL类验证某个字符串是否是标准的URL格式。
forms.py修改如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, URL
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
info_page = StringField(validators=[URL(message='地址格式不正确')])
login.html修改如下:
登录
个人主页:
显示:
要验证验证码长度和有效性,长度用Length验证,有效性可以在表单类中自定义方法即可。
forms.py修改如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo, ValidationError
class RegisterForm(Form):
username = StringField(validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField(validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField(validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
class LoginForm(Form):
captcha = StringField(validators=[Length(min=4, max=4, message='验证码不正确')])
def validate_captcha(self, field):
'''自定义验证,方法名为固定格式,即validate_加要验证的变量名'''
if field.data != '1234':
raise ValidationError("验证码错误")
login.html修改如下:
登录
验证码:
显示:
wtforms可以渲染模板,省去一部分代码。
测试:
wtf_demo.py如下:
from flask import Flask, request, render_template
from forms import RegisterForm
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@app.route('/register/', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method == 'GET':
return render_template('register.html', form=form)
else:
username = form.username.data
password = form.password.data
pwd_confirm = form.pwd_confirm.data
if form.validate():
return '验证成功--User: ' + username + ' Password: ' + password
else:
return '验证失败:\n' + str(form.errors)
if __name__ == '__main__':
app.run(debug=True)
在渲染模板的时候传入了form表单参数,这样在模板中就可以使用表单form变量了。
forms.py如下:
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo
class RegisterForm(Form):
username = StringField('用户名', validators=[Length(min=3, max=15, message='用户名长度不正确')])
password = StringField('密码', validators=[Length(min=6, max=20, message='密码长度不正确')])
pwd_confirm = StringField('密码确认', validators=[Length(min=6, max=20), EqualTo('password', message='两次密码不一致')])
每个变量都增加了一个位置参数,用于在html文件中做标签提示。
register.html如下:
注册
{{ form.username.label }}
{{ form.username() }}
{{ form.password.label }}
{{ form.password() }}
{{ form.pwd_confirm.label }}
{{ form.pwd_confirm() }}
显示:
先创建项目目录flask_upload,创建主文件flask_app.py如下:
from flask import Flask, request,render_template
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
if request.method == 'GET':
return render_template('upload.html')
else:
desc = request.form.get('desc')
image_file = request.files.get('image_file')
file_name = secure_filename(image_file.filename)
image_file.save('files/images/' + file_name)
return '文件上传成功--' + desc
if __name__ == '__main__':
app.run(debug=True)
创建templates目录,下面创建upload.html如下:
文件上传
{# 文件上传,form表单必须要添加enctype属性 #}
头像
描述
在项目目录下创建files文件夹用于保存上传的文件,下面创建images子目录,运行主程序之后,显示:
显然,文件上传成功。
我们可以得到:
在模版中的form标签中,需要指定enctype='multipart/form-data'
才能上传文件;
在后台如果想要获取上传的文件,应该使用request.files.get(file_name)
来获取;
保存文件之前,先要使用werkzeug.utils类中的secure_filename()
方法来对上传上来的文件名进行过滤,以确保安全性;
获取到上传上来的文件后,使用filename.save(路径)
方法来保存文件。
可以看到,在上传的文件中,纯英文文件名保留不变,有中文的将中文去掉,这是由于werkzeug.utils模块中的secure_filename方法只支持ASCII编码的字符,这个问题是可以解决的,可参考https://blog.csdn.net/qq_36390239/article/details/98847888解决。
2.文件上传的表单验证上面虽然上传文件成功,但是并未对文件进行验证,存在很大的风险。
进行文件验证测试:
新建forms.py如下:
from wtforms import Form, FileField, StringField
from wtforms.validators import InputRequired
from flask_wtf.file import FileAllowed, FileRequired
class UploadForm(Form):
image_file = FileField(validators=[FileRequired(message='文件必须上传'), FileAllowed(['jpg', 'png', 'gif'], message='上传文件格式有误')])
desc = StringField(validators=[InputRequired('必须填写描述')])
修改flask_app.py如下:
from flask import Flask, request,render_template
from werkzeug.utils import secure_filename
from werkzeug.datastructures import CombinedMultiDict
from forms import UploadForm
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@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():
desc = request.form.get('desc') # 等价于desc = form.desc.data
image_file = request.files.get('image_file') # 等价于image_file = form.image_file.data
file_name = secure_filename(image_file.filename)
image_file.save('files/images/' + file_name)
return '文件上传成功--' + desc
else:
return '文件上传失败--' + str(form.errors)
if __name__ == '__main__':
app.run(debug=True)
显示:
显然,已经实现了文件上传时的验证,其中:
desc = request.form.get('desc')
image_file = request.files.get('image_file')
等价于
desc = form.desc.data
image_file = form.image_file.data
可以得到:
定义表单的时候,对文件的字段需要使用FileField;
验证器从flask_wtf.file中导入,FileRequired是用来验证文件上传是否为空,FileAllowed用来验证上传的文件的后缀名;
在视图文件中,使用werkzeug.datastructures类中的CombinedMultiDict()
方法将request.form与request.files合并,再传给表单进行验证。
想要读取上传的文件时,需要定义一个视图函数,来获取指定的文件。
在这个视图函数中,使用send_from_directory(文件的目录, 文件名)
来获取。
flask_app.py如下:
from flask import Flask, request, render_template, send_from_directory
from werkzeug.utils import secure_filename
from werkzeug.datastructures import CombinedMultiDict
from forms import UploadForm
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@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():
desc = request.form.get('desc') # 等价于desc = form.desc.data
image_file = request.files.get('image_file') # 等价于image_file = form.image_file.data
file_name = secure_filename(image_file.filename)
image_file.save('files/images/' + file_name)
return '文件上传成功--' + desc
else:
return '文件上传失败--' + str(form.errors)
@app.route('/images/')
def get_image(filename):
return send_from_directory('files/images/', filename)
if __name__ == '__main__':
app.run(debug=True)
显示:
很明显,成功访问到了资源。
在网站中,http请求是无状态的,也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。
cookie的出现就是为了解决这个问题,类型为小型文本文件,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。
第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动将上次请求存储的cookie数据自动传送给服务器,服务器通过浏览器携带的数据来判断用户。
cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。
在Flask中操作cookie,是通过Response对象来操作,可以在response返回之前,通过response.set_cookie()
方法来设置,这个方法有以下几个参数:
cookie简单测试:
flask_app.py如下:
from flask import Flask, Response
app = Flask(__name__)
app.config['TEMPLATE_AUTO_RELOAD'] = True
@app.route('/')
def index():
return '首页'
@app.route('/cookietest/')
def cookietest():
res = Response('Hello World!!')
res.set_cookie('username', value='corley', max_age=3)
return res
if __name__ == '__main__':
app.run(debug=True)
显示:
cutercorley 原创文章 129获赞 1434访问量 35万+ 关注 私信 展开阅读全文