Flask 框架 表单验证 & cookie/session

在这里插入图片描述

Flask-WTF

Flask-WTF 是简化了 WTForms 操作的一个第三方库

WTForms 表单的两个主要功能是验证用户提交数据的合法性以及渲染模板,当然也有其他的功能,如 CSRF 保护,文件上传等

安装 Flask-WTF 默认也会安装 WTForms,因此下列命令会安装二个库

pip install flask-wtf  # 同时安装 Flask-WTF 和 WTForms

借用 WTForms 表单验证

由于下列文件执行过程出现错误,我们再次下载一个库,执行命令

pip install email_validator

Python 主文件(demo.py)

from flask import Flask, request, render_template
# 用于创建 Flask 实例, 请求 url 参数, 连接 html 文件
from forms import RegisterForm, LoginForm
# 用于表单验证, 从 form.py 文件中导入 registerForm, LoginForm 类

web = Flask(__name__)  # 实例化 Flask 对象


@web.route('/')  # 关联视图函数(url)
def index():  # 视图函数
    return '首页'  # 浏览器接收字符串


@web.route('/register/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def register():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('register.html')  # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
        form = RegisterForm(request.form)  # 实例化表单类, 并传递参数 request.form(post)
        if form.validate():  # 表单执行无错误
            return '成功'
        else:  # 表单执行出错
            print(form.errors)  # 打印错误
            return '失败'


@web.route('/login/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def login():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('login.html')  # # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
        form = LoginForm(request.form)  # 实例化表单类, 并传递参数 request.form(post)
        if form.validate():  # 表单执行无错误
            return '成功'
        else:  # 表单执行出错
            print(form.errors)  # 打印错误
            return '失败'


if __name__ == '__main__':
    web.run(debug=True)  # 运行 Flask 实例

Python 表单文件(forms.py)

from wtforms import Form, StringField  # 用于创建表单和创建数据类型
from wtforms.validators import Length, EqualTo, Email  # 用于创建数据关联


class RegisterForm(Form):
    # StringField 是定义字符串类型的方法
    username = StringField(validators=[Length(min=3, max=10, message='用户长度不正确')])
    # Length 里面定义了该字段 username 长度的最小值和最大值, 以及出错返回信息
    password = StringField(validators=[Length(min=3, max=10, message='密码长度不正确')])
    # Length 里面定义了该字段 password 长度的最小值和最大值, 以及出错返回信息
    password_verify = StringField(validators=[Length(min=3, max=10, message='密码长度不正确'), EqualTo('password', message='两次密码不一致')])
    # Length 里面定义了该字段 password_verify 长度的最小值和最大值, 以及出错返回信息, 同时还用 EqualTo 方法和字段 password 进行比较, 如果二个字段值不一样, 则返回错误信息


class LoginForm(Form):
    email = StringField(validators=[Email(message='邮箱不正确')])
    # 此处使用 Email 方法和 StringField 方法

login.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <div>
        <span>邮箱:</span><input type="text" name="email">
		<!--  此处 name='email' 主要用和 post 提交过程中数据的传递  -->
        <input type="submit" value="提交">
    </div>
</form>
</body>
</html>

register.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <div>
        <span>用户名:</span><input type="text" name="username">
        <!--  此处 name='email' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <span>密码:</span><input type="text" name="password">
        <!--  此处 name='password' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <span>确认密码:</span><input type="text" name="password_verify">
        <!--  此处 name='password_verify' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <input type="submit" value="注册">
    </div>
</form>
</body>
</html>

不借用 WTForms 表单的验证过程

Python 主文件(二个 html 和上面的一样)

from flask import Flask, request, render_template
# 用于创建 Flask 实例, 请求 url 参数, 连接 html 文件

web = Flask(__name__)  # 实例化 Flask 对象


@web.route('/')  # 关联视图函数(url)
def index():  # 视图函数
    return '首页'  # 浏览器接收字符串


@web.route('/register/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def register():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('register.html')  # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
        # 直接用 request.form.get 方法获取关键字参数
        username = request.form.get('username')
        password = request.form.get('password')
        password_verify = request.form.get('password_verify')
        # 验证表单数据的正确性并返回结果
        if len(username) < 3 or len(username) > 10:
            return '用户名长度不正确'
        elif len(password) < 3 or len(password) > 10:
            return '密码长度不正确'
        elif password != password_verify:
            return '二次输入不一致'
        else:
            return '注册成功'


@web.route('/login/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def login():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('login.html')  # # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
    	# 直接用 request.form.get 方法获取关键字参数
        email = request.form.get('email')
        # 验证表单数据的正确性并返回结果
        if '@' in email:
            return '登录成功'
        else:
            return '登录失败'




if __name__ == '__main__':
    web.run(debug=True)  # 运行 Flask 实例

细讲表单验证

Flask-WTF 第一个功能,就是用表单来做数据验证,现在有一个 forms.py 文件,然后在里面创建一个 RegisterForm 的注册验证表单和 LoginForm 邮箱登录验证表单

from wtforms import Form, StringField  # 用于创建表单和创建数据类型
from wtforms.validators import Length, EqualTo, Email  # 用于创建数据关联


class RegisterForm(Form):
    # StringField 是定义字符串类型的方法
    username = StringField(validators=[Length(min=3, max=10, message='用户长度不正确')])
    # Length 里面定义了该字段 username 长度的最小值和最大值, 以及出错返回信息
    password = StringField(validators=[Length(min=3, max=10, message='密码长度不正确')])
    # Length 里面定义了该字段 password 长度的最小值和最大值, 以及出错返回信息
    password_verify = StringField(validators=[Length(min=3, max=10, message='密码长度不正确'), EqualTo('password', message='两次密码不一致')])
    # Length 里面定义了该字段 password_verify 长度的最小值和最大值, 以及出错返回信息, 同时还用 EqualTo 方法和字段 password 进行比较, 如果二个字段值不一样, 则返回错误信息


class LoginForm(Form):
    email = StringField(validators=[Email(message='邮箱不正确')])
    # 此处使用 Email 方法和 StringField 方法

其中指定了需要上传的参数,并且指定了验证器,比如 username 的长度应该在 3-10 之间,email 必须要满足邮箱的格式,password 长度必须在 3-10 之间,password_verify 长度必须在 3 ~ 10 之间,并且应该和 password 一样才能通过验证

表单完成之后,接下来写二个 html 文件,特别强调,注意表单中的参数需要和 html 中标签的 name 属性对应!

其一,register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <div>
        <span>用户名:</span><input type="text" name="username">
        <!--  此处 name='email' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <span>密码:</span><input type="text" name="password">
        <!--  此处 name='password' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <span>确认密码:</span><input type="text" name="password_verify">
        <!--  此处 name='password_verify' 主要用和 post 提交过程中数据的传递  -->
    </div>
    <div>
        <input type="submit" value="注册">
    </div>
</form>
</body>
</html>

其二,login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <div>
        <span>邮箱:</span><input type="text" name="email">
		<!--  此处 name='email' 主要用和 post 提交过程中数据的传递  -->
        <input type="submit" value="提交">
    </div>
</form>
</body>
</html>

最后,视图文件,RegisterForm 传递的是 request.form ,用于初始化,并且判断 form.validate 会返回用户提交的数据是否满足表单的验证

from flask import Flask, request, render_template
# 用于创建 Flask 实例, 请求 url 参数, 连接 html 文件
from forms import RegisterForm, LoginForm
# 用于表单验证, 从 form.py 文件中导入 registerForm, LoginForm 类

web = Flask(__name__)  # 实例化 Flask 对象


@web.route('/')  # 关联视图函数(url)
def index():  # 视图函数
    return '首页'  # 浏览器接收字符串


@web.route('/register/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def register():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('register.html')  # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
        form = RegisterForm(request.form)  # 实例化表单类, 并传递参数 request.form(post)
        if form.validate():  # 表单执行无错误
            return '成功'
        else:  # 表单执行出错
            print(form.errors)  # 打印错误
            return '失败'


@web.route('/login/', methods=['GET', 'POST'])  # 关联视图函数(url), 指定 url 获取方法为 post 和 get
def login():  # 视图函数
    if request.method == 'GET':  # 若获取方法为 get
        return render_template('login.html')  # # 连接 html 文件, 要注意此文件的存放位置
    else:  # 若获取方法为 post
        form = LoginForm(request.form)  # 实例化表单类, 并传递参数 request.form(post)
        if form.validate():  # 表单执行无错误
            return '成功'
        else:  # 表单执行出错
            print(form.errors)  # 打印错误
            return '失败'


if __name__ == '__main__':
    web.run(debug=True)  # 运行 Flask 实例

文件上传

普通上传

Python 主文件

from werkzeug.datastructures import CombinedMultiDict
from froms import UploadForm
from flask import Flask, request, render_template  # 用于 Flask 实例, 请求 url, 连接 html
from werkzeug.utils import secure_filename  # 用于判断文件名的正确性和规范性
import os  # 用于打开文件路径

web = Flask(__name__)  # 创建 Flask 实例


@web.route('/')  # 连接视图函数
def index():  # 视图函数
    return '首页'  # 渲染文本


@web.route('/upload/', methods=['GET', 'POST'])  # 连接视图函数, 指定数据获取方法为 get/post
def upload():  # 视图函数
    if request.method == 'GET':  # 如果数据获取方法为 get
        return render_template('upload.html')  # 连接 html 文件, 注意文件目录
    else:  # 如果数据获取方法为 post
        desc = request.form.get('desc')  # 获取 html 文件表单中的 input 标签(name=desc)
        # request.files.get  接受文件的形式
        # 获取 html 文件表单中的 input 标签(name=image_file)
        image_file = request.files.get('image_file')  # image_file 接收用 request.files.get 打开的文件
        filename = secure_filename(image_file.filename)  # secure_filename 用来判断文件名的正确性和规范性
        image_file.save(os.path.join('images', filename))  # 保存文件在方法 os.path.join 指定的目录 images 中
        return '文件上传成功'  # 若上述过程无错误, 返回此字符串


if __name__ == '__main__':
    web.run(debug=True, port=9000)

upload.html 文件

enctype='multipart/form-data'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" enctype='multipart/form-data'>
<!--    enctype='multipart/form-data' 用于和 python 文件获取数据关联-->
    <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>

使用表单上传

表单文件

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(), FileAllowed(['jpg', 'gif', 'png'])])
    desc = StringField(validators=[InputRequired()])

Python 主文件

from werkzeug.datastructures import CombinedMultiDict 
from froms import UploadForm  # 导入表单文件  html 
from flask import Flask, request, render_template  # 用于 Flask 实例, 请求 url, 连接 html
from werkzeug.utils import secure_filename  # 用于判断文件名的正确性和规范性
import os  # 用于打开文件路径

web = Flask(__name__)  # 创建 Flask 实例


@web.route('/')  # 连接视图函数
def index():  # 视图函数
    return '首页'  # 渲染文本


@web.route('/upload/', methods=['GET', 'POST'])  # 连接视图函数, 指定数据获取方法为 get/post
def upload():  # 视图函数
    if request.method == 'GET':  # 如果数据获取方法为 get
        return render_template('upload.html')  # 连接 html 文件, 注意文件目录
    else:  # 如果数据获取方法为 post
        form = UploadForm(CombinedMultiDict([request.form, request.files]))
        if form.validate():  # 执行过程无错误
            desc = request.form.get('desc')
            # request.files.get  接受文件的形式
            image_file = request.files.get('image_file')
            filename = secure_filename(image_file.filename)
            image_file.save(os.path.join('images', filename))
            return '文件上传成功'
        else:
            print(form.errors)
            return '文件上传失败'


if __name__ == '__main__':
    web.run(debug=True, port=9000)

upload.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" enctype='multipart/form-data'>
<!--    enctype='multipart/form-data' 用于和 python 文件获取数据关联-->
    <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>

cookie 和 session

cookie

在网站中,http 请求是无状态的,也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户,cookie 的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie 数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了

cookie 存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过 4KB,因此使用 cookie 只能存储一些小量的数据

session

session 和 cookie 的作用有点类似,都是为了存储用户相关的信息,不同的是,cookie 是存储在本地浏览器,session 是存储在服务器,虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的,session 的出现,是为了解决 cookie 存储数据不安全的问题

cookie 和 session 结合使用

web 开发发展至今,cookie 和 session 的使用已经出现了一些非常成熟的方案,在如今的市场或者企业里,一般有两种存储方式

存储在服务端

通过 cookie 存储一个 session_id,然后具体的数据则是保存在 session 中,如果用户已经登录,则服务器会在 cookie 中保存一个session_id,下次再次请求的时候,会把该 session_id 携带上来,服务器根据 session_id 在 session 库中获取用户的 session 数据,就能知道该用户到底是谁,以及之前保存的一些状态信息,这种专业术语叫做 server side session,存储在服务器的数据会更加的安全,不容易被窃取,但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器应对一些 session 信息还是绰绰有余的

将 session 数据加密,然后存储在 cookie 中

这种专业术语叫做 client side session,flask 采用的就是这种方式,但是也可以替换成其他形式

cookie 实例创建过程

如下,我们利用 pycharm 来为 Flask 框架创建 cookie,具体实现过程在视图函数内,具体语法是,用变量接收 Response 实例化的对象,并且传入一个参数,然后使用该变量的 set_cookie 方法,并且接收三个参数,分别是 cookie 名称,cookie 内容及有效时间

from flask import Flask, Response  # 用于创建 Flask 实例, 设置 cookie

web = Flask(__name__)  # 创建 Flask 实例


@web.route('/')  # 连接视图函数
def index():  # 视图函数
    res = Response('百度')  # 实例化对象, 用变量 res 接收
    res.set_cookie('username', 'xiao su', max_age=60*60*8)  # max_age 设置的是秒, 此处设置的是 8h
    return res  # 浏览器渲染字符串 '百度'


if __name__ == '__main__':
    web.run(debug=True)  # 运行 Flask 实例

在这里插入图片描述
在这里插入图片描述
session 实例创建过程

如下,我们用 session 来创建一个加密的 cookie,首先需要给 Flask 实例 web,设置密钥为一段随机的字符串,设置其 session 保存时间为 24 h,另外 session 的封装是在视图函数 login 中进行的,语法为 session['名称'] = '内容',session 退出语法为 session.clear()

from flask import Flask, session  # 用于 Flask 实例和创建 session 实例
from datetime import timedelta  # 用于设置 session 存活时间
import os  # 用于随机生成一段字符串

web = Flask(__name__)  # 用于 Flask 实例

print(os.urandom(5))  # 输出一段长度为 5 的随机字符串
web.config['SECRET_KEY'] = os.urandom(25)  # 设置密匙为一段随机生成的字符串
web.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=24)  # session 存活时间配置


@web.route('/')  # 连接视图函数
def index():  # 视图函数
    return '首页'  # 渲染文本


@web.route('/login/')  # 连接视图函数
def login():  # 视图函数
    # 一个字典的形式进行存储的
    session['username'] = 'xiao ke'
    # session 持久化(时间为 30 days)
    session.permanent = True
    return '登录页面'  # 渲染文本


@web.route('/logout/')  # 连接视图函数
def logout():  # 视图函数
    # session 清除方式 :clear()
    session.clear()  # 清除当前 session
    return '退出登录'  # 渲染文本


if __name__ == '__main__':
    web.run(debug=True, port=8000)  # 运行 Flask 实例

此处显示的正在使用的 Cookie 在 url 为 / 或 /login/ 时始终只有一个加密的 session 存在

在这里插入图片描述
而当我们 url 改为 /logout/ 后,Cookie 则会被清除

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是我来晚了!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值