Python全栈(七)Flask框架之11.WTForms及其应用和Cookie的简单使用
文章目录
flask-wtf是一个简化了WTFForms操作的第三方库,WTForms表单的两个主要功能是验证用户提交数据的合法性和渲染模板,还包括一些其他的功能,如CSRF保护、文件上传等。
安装flask-wtf使用命令
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) < 3 or len(username) > 15: return '用户名长度不正确' if password != pwd_confirm: return '两次密码输入不一致' if len(username) < 6 or len(username) > 20: return '密码长度不正确' if __name__ == '__main__': app.run(debug=True)
模板目录templates下创建register.html如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> </head> <body> <form action="" method="post"> 用户名:<input type="text" name="username"> <br> 密码:<input type="text" name="password"> <br> 确认密码:<input type="text" name="pwd_confirm"> <br> <input type="submit" value="注册"> </form> </body> </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相等才能通过验证。
2.Flask-WTF常用的验证器
可以直接使用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如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>邮箱:</td> <td><input type="text" name="email"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
运行主程序,显示:
Number
可以通过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如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>年龄:</td> <td><input type="text" name="age"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </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修改如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </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修改如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>手机号:</td> <td><input type="text" name="phone_number"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
显示:
URL
可以使用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修改如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>个人主页:</td> <td><input type="text" name="info_page"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </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修改如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="" method="post"> <table> <tr> <td>验证码:</td> <td><input type="text" name="captcha"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
显示:
二、WTF渲染模板
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如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> </head> <body> <form action="" method="post"> <table> <tr> <td>{{ form.username.label }}</td> <td>{{ form.username() }}</td> </tr> <tr> <td>{{ form.password.label }}</td> <td>{{ form.password() }}</td> </tr> <tr> <td>{{ form.pwd_confirm.label }}</td> <td>{{ form.pwd_confirm() }}</td> </tr> <tr> <td></td> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
显示:
三、WTF文件上传
1.文件上传的基本使用
先创建项目目录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如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body> {# 文件上传,form表单必须要添加enctype属性 #} <form action="" method= 20000 "post" enctype="multipart/form-data"> <table> <tr> <td>头像</td> <td><input type="file" name="image_file"></td> </tr> <tr> <td>描述</td> <td><input type="text" name="desc"></td> </tr> <tr> <td><input type="submit" value="上传"></td> </tr> </table> </form> </body> </html>
在项目目录下创建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合并,再传给表单进行验证。
3.访问上传资源
想要读取上传的文件时,需要定义一个视图函数,来获取指定的文件。
在这个视图函数中,使用
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/<filename>') def get_image(filename): return send_from_directory('files/images/', filename) if __name__ == '__main__': app.run(debug=True)
显示:
很明显,成功访问到了资源。
四、Cookie的使用
1.Cookie的基本概念
在网站中,http请求是无状态的,也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。
cookie的出现就是为了解决这个问题,类型为小型文本文件,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。
第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动将上次请求存储的cookie数据自动传送给服务器,服务器通过浏览器携带的数据来判断用户。
cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。
2.Flask中使用Cookie
在Flask中操作cookie,是通过Response对象来操作,可以在response返回之前,通过
response.set_cookie()方法来设置,这个方法有以下几个参数:
- key
cookie的键
value
cookie的键对应的值。 - max_age
cookie的过期时间,如果不设置,则浏览器关闭后就会自动过期。 - expires
过期时间,时间戳的形式(1970到现在的时间)。 - domain
该cookie在哪个域名中有效,一般设置子域名,比如cms.example.com。 - path
该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获赞 1432访问量 34万+ 关注 私信
- Python全栈(七)Flask框架之10.ORM插件、Script、Migrate和WTF的简单使用
- python--flask框架的安装和简单使用
- python3使用flask框架搭建在线词云应用
- python 全栈开发,Day142(flask标准目录结构, flask使用SQLAlchemy,flask离线脚本,flask多app应用,flask-script,flask-migrate,p...
- 关于Python对简单的web框架flask的应用使得不只是本机而是局域网都能访问
- 使用Python Flask框架快速实现简单API
- Python全栈(七)Flask框架之6.数据库介绍和SQLAlchemy使用
- 在Python的Flask框架下使用sqlalchemy库的简单教程
- 在python的WEB框架Flask中使用多个配置文件的解决方法
- Python Web框架Flask中使用新浪SAE云存储实例
- 使用Python的Flask框架表单插件Flask-WTF实现Web登录验证
- python常用web框架简单性能测试结果分享(包含django、flask、bottle、tornado)
- Linux中ifreq 结构体分析和使用 及其在项目中的简单应用
- 使用Python的Flask框架构建大型Web应用程序的结构示例
- 使用Python Falcon框架快速实现简单API
- Python Web框架Flask中使用百度云存储BCS实例
- 使用Python的Flask框架实现视频的流媒体传输
- python-flask简单服务器框架
- 使用react-native做一个简单的应用-04界面主框架
- Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程