网站首页 > 技术文章 正文
上一章节我们通过在html中直接编写表单的方式进行数据传递,并且在视图中对前端传递的数据进行了简单的认证,但是如果把验证数据的代码与逻辑混合在一起,将使得视图的代码不够清晰,并且难以维护,稍加疏忽就会产生验证漏洞,如果细心的同学其实可以发现,在之前的登录注册中我们一直没有对空表单进行验证,当然这是我故意为之,但如果在生产环境,这将是一个灾难的开始,所以,在编程中无论是前端还是后端都要求要对数据进行验证,作为后端,更要保持一种永远不相信前端传递数据的态度去做数据校验。
本章节我们将使用Flask官方推荐的Flask-WTF扩展来重构我们的登录注册表单!
关于Flask-WTF
Flask-WTF是Flask 和 WTForms 的简单集成,包括 CSRF、文件上传和 reCAPTCHA。
- 安装Flask-WTF:
pip install Flask-WTF
创建登录注册表单类
在app/auth/目录下新建一个forms.py的文件,所有的表单验证代码都放到这个文件当中!
构建登录表单
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Length, ValidationError, EqualTo
from werkzeug.security import check_password_hash
from .models import User
class LoginForm(FlaskForm):
# 登录表单
def qs_username(username):
# 对该字段进行在传递之前处理
u = f'{username}123456'
print(u)
return username
username = StringField('username', validators=[
DataRequired(message="不能为空"),
Length(max=32, message="不符合字数要求!")
], filters=(qs_username,))
password = PasswordField('password', validators=[
DataRequired(message="不能为空"),
Length(max=32, message="不符合字数要求!")
])
def validate_username(form, field):
user = User.query.filter_by(username=field.data).first()
if user is None:
error = '该用户不存在!'
raise ValidationError(error)
elif not check_password_hash(user.password, form.password.data):
raise ValidationError('密码不正确')
代码详解:
class LoginForm(FlaskForm): 创建了一个登录表单类,继承了FlaskForm类
StringField, PasswordField
这些都是wtforms内置的字段,负责呈现和数据转换。
官方文档:https://wtforms.readthedocs.io/en/3.0.x/fields/
他继承自Filed的基类,其中有一些比较重要的参数我们大概在这里理解一下!
第一个字符串其实是该类的label参数,字段的标签,也就是转换到html中的label!
validators传入对该字段的一些验证器,在提交数据之前对数据进行验证!
filters这个参数比较特殊,官方文档并没有对其详细说明,只说是筛选器,其实怎么说就是在额外的方法中对该字段的值提前处理过滤,元组中的每个值都是一个回调函数,函数不需要传入括号,但这个回调函数默认有一个参数,这个参数就是本身该字段的值,所以在定义该函数时就必须传入一个参数!例如:我们定义username之前定义的这个方法!
def qs_username(username):
# 对该字段进行在传递之前处理
u = f'{username}123456'
print(u)
return username
备注:必须返回处理后的这个参数,否则会触发DataRequired验证器,后端获取不到该表单的值!
- DataRequired, Length 这是内置的验证器,第一个是验证字段是否为空,第二个Length是验证输入长度,当然内置的还有很多,这里就不一一列举,具体我们可参考文档!
官方文档: https://wtforms.readthedocs.io/en/3.0.x/validators/#custom-validators
自定义验证用户名和密码
在之前的视图函数中我们对用户名和密码都做了校验,现在我们需要把验证的代码全部移动到表单类中,代码如下:
def validate_username(form, field):
user = User.query.filter_by(username=field.data).first()
if user is None:
error = '该用户不存在!'
raise ValidationError(error)
elif not check_password_hash(user.password, form.password.data):
raise ValidationError('密码不正确')
- validate_username(form, field)
这个函数的写法是固定的validate_{filed},validate_后边的filed是指你需要验证的某个字段名,比如我们这个验证,他主要就是对username字段进行验证,这个函数中参数的filed就是这个字段,通过field.data就可以获取到usernam的值。form参数则指代的是整个表单,可以用form.{filed}.data的方式获取表单类中某个具体字段的值!
构建注册表单
当了解了登录表单后,我们完全就可以参照登录表单去实现注册表单,代码如下:
路径:app/auth/forms.py
class RegisterForm(FlaskForm):
# 注册表单
username = StringField('username', validators=[
DataRequired(message="不能为空"),
Length(min=2, max=32, message="超过限制字数!")
])
password = PasswordField('password', validators=[
DataRequired(message="不能为空"),
Length(min=2, max=32, message="超过限制字数!"),
EqualTo('password1', message='两次密码输入不一致!')
])
password1 = PasswordField('password1')
def validate_username(form, field):
user = User.query.filter_by(username=field.data).first()
if user is not None:
error = '该用户名称已存在!'
raise ValidationError(error)
这里唯一需要注意的是两次密码是否输入一致,我们用了一个内置的验证器EqualTo,使用方式可完全参照代码,他会自动校验password和password1输入的值是否一致!
重构登录和注册视图
- 路径:app/auth/views/auth.py
from ..forms import LoginForm, RegisterForm
@bp.route('/login', methods=['GET', 'POST'])
def login():
# 登录视图
# form = LoginForm(meta={'csrf': False}) # 禁用csrf
form = LoginForm()
if form.validate_on_submit():
user = auth.User.query.filter_by(username=form.username.data).first()
session.clear()
session['user_id'] = user.id
return redirect(url_for('index'))
return render_template('login.html', form=form)
@bp.route('/register', methods=['GET', 'POST'])
def register():
# 注册视图
form = RegisterForm()
if form.validate_on_submit():
user = auth.User(username=form.username.data, password=generate_password_hash(form.password.data))
db.session.add(user)
db.session.commit()
session.clear()
session['user_id'] = user.id
return redirect(url_for('index'))
return render_template('register.html', form=form)
1、首先从forms.py中引入了我们定义的登录(LoginForm)和注册(RegisterForm)表单类!
2、form = RegisterForm() 实例化表单类
3、if form.validate_on_submit(): 验证前端传递的数据是否有效,并且会自动判断是POST请求还是GET请求!
4、 数据验证通过则进入之后的逻辑,未验证通过则返回我们在表单类中传入的验证提示!
模板中调用验证信息
我们以调用username字段的验证提示为例,在模板中加入这段代码即可获得错误提示!
<!-- 表单验证 -->
{% if form.username.errors %}
<b-message type="is-danger">
<ul class="errors">
{% for error in form.username.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</b-message>
{% endif %}
重构登录注册html模板
路径:app/auth/templates/login.html 以登陆表单为例,代码如下:
{% block auth_form %}
<form action="" method="post" style="margin-top: 40%;" class="box">
<div class=" has-text-centered mb-3">
<p class=" subtitle">登录</p>
<h1 class="title">FlaskBlog</h1>
</div>
{{ form.csrf_token }}
<!-- 消息闪现 -->
{% with messages = get_flashed_messages() %}
<b-message type="is-danger">
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
</b-message>
{% endwith %}
<!-- 表单验证 -->
{% if form.username.errors %}
<b-message type="is-danger">
<ul class="errors">
{% for error in form.username.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</b-message>
{% endif %}
<div class="field">
<p class="control has-icons-left has-icons-right">
{{ form.username(class='input', placeholder='Username') }}
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
<span class="icon is-small is-right">
<i class="fas fa-check"></i>
</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
{{ form.password(class='input', placeholder='Password') }}
<span class="icon is-small is-left">
<i class="fas fa-lock"></i>
</span>
</p>
</div>
<div class="field">
<p class="control">
<input class="button is-success is-fullwidth" type="submit" value="Login">
</p>
</div>
</form>
{% endblock auth_form %}
{{ form.csrf_token }} 隐式的创建一个csrftoken的表单
{{ form.username(class='input', placeholder='Username') }} 这样就可以直接获得一个表单html并自动渲染,向该表单增加书香的方式就是像代码中这样传入参数和值即可,当然也可以提前在表单类中定义!
剩下的注册表单,就当是给大家留作的一个作业,大家自行去参照登录表单完善重构一下,加油哦!我相信你可以!
到这里我们的表单验证就大概了解了,之后的章节就是基本的增删改查以及表单验证,都是基于我们这些章节学习的知识点,所以之后的章节就不会过多的去讲解每行代码的意思,重心放在逻辑的展示上,如果基础较差的同学,到这里,可以去反复的把前边所有章节的内容去练习,写代码其实就是写的多了就会了,也就理解了,练习 练习 再练习!
猜你喜欢
- 2024-10-15 Python Flask Web表单(flask form表单)
- 2024-10-15 如何在Flask应用程序中使用JSON Web Tokens进行安全认证
- 2024-10-15 综合指南:将 Flask 与 MongoDB 结合使用
- 2024-10-15 python-flask搭建平台,HTML+CSS+JS写前端的web全栈-动画轮播
- 2024-10-15 Flask 表单处理(flask formdata)
- 2024-10-15 三、flask博客项目实战-之表单(flask 表单)
- 2024-10-15 flask python web开发的简单易学框架
- 2024-10-15 Flask-APScheduler使用教程(flask apscheduler)
- 2024-10-15 flask 项目中使用 bootstrapFileInput(进阶篇)
- 2024-10-15 基于flask框架展示数据可视化的一次尝试
- 最近发表
-
- 如何在 Linux 上安装 Java_怎么在linux中安装jdk
- Linux中tar命令打包路径相关问题_linux怎么用tar打包一个目录
- 常用linux系统常用扫描命令汇总_常用linux系统常用扫描命令汇总表
- VM下linux虚拟机新建过程(有图)_linux虚拟机创建新用户命令
- 系统小技巧:迁移通过Wubi方式安装的Ubuntu系统
- 文件系统(八):Linux JFFS2文件系统工作原理、优势与局限
- 如何利用ftrace精确跟踪特定进程调度信息
- prometheus网络监控之fping-exporter
- hyper linux的实操步骤,hyper-v批量管理工具的使用指南
- 2021年,运维工程师笔试真题(二)(附带答案)
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)