(二十八)Flask之wtforms库【上手使用篇】

目录:

  • 每篇前言:
  • 用户登录验证:
  • 用户注册验证:
    • 使用示例:
  • 抽象解读使用wtforms编写的类:
    • 简单谈一嘴:
    • 开始抽象:

每篇前言:

  • 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者

  • 🔥🔥本文已收录于Flask框架从入门到实战专栏:《Flask框架从入门到实战》
  • 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
  • 📝​📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
  • 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
  • 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!

在这里插入图片描述

WTForms 是一个用于处理Web表单的Python库。它设计简单,易于使用,广泛用于Web应用程序的表单处理,特别是与Flask等框架一起使用。

wtforms依照功能类别来说有以下几个类别:

  • Forms: 主要用于表单验证、字段定义、HTML生成,并把各种验证流程聚集在一起进行验证。
  • Fields: 主要负责渲染(生成HTML)和数据转换。
  • Validator:主要用于验证用户输入的数据的合法性。比如Length验证器可以用于验证输入数据的长度。
  • Widgets:html插件,允许使用者在字段中通过该字典自定义html小部件。
  • Meta:用于使用者自定义wtforms功能,例如csrf功能开启。
  • Extensions:丰富的扩展库,可以与其他框架结合使用,例如django。
pip install wtforms==2.1

官方文档:

  • https://wtforms.readthedocs.io/en/stable/index.html#

用户登录验证:

  • 当用户登录时,需要对用户提交的用户名和密码进行多种格式校验,如:

    用户名不能为空;长度必须大于6;

    密码不能为空;长度必须大于12;密码必须包含字母、数字、特殊字符等(通过正则自定义)…

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core, simple
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass LoginForm(Form):user = simple.StringField(label='用户名',validators=[validators.DataRequired(message='用户名不能为空'),validators.Length(min=6, max=18, message=f'用户名长度必须大于{min}且小于{max}')],widget=widgets.TextInput(),render_kw={'class': 'form-control'}    # 设置生成的html标签的属性)pwd = simple.PasswordField(label='密码',validators=[validators.DataRequired(message='密码不能为空'),validators.Length(min=8, message=f'密码必须大于{min}'),# validators.Regexp(regex=r'^(?=.*[A-Z])'  # 至少一个大写字母#                         r'(?=.*[a-z])'  # 至少一个小写字母#                         r'(?=.*\d)'  # 至少一个数字#                         r'(?=.*[@$!%*?&])'  # 至少一个特殊字符#                         r'[A-Za-z\d@$!%*?&]{8,}$',  # 总长度至少8个字符#                   message='密码至少8个字符,至少一个大写字母,一个小写字母,一个数字和一个特殊字符')],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'GET':form = LoginForm()return render_template('login.html', form=form)form = LoginForm(formdata=request.form)if not form.validate():return render_template('login.html', form=form)# 对用户提交数据进行校验,form.data是校验完成后的数据字典if form.data['user'] == '1234567' and form.data['pwd'] == '123456789':print('用户提交的数据通过格式验证,提交的值为:', form.data)return 'Login OK~'else:return render_template('login.html',msg='用户名或密码错误', form=form)if __name__ == '__main__':app.run()

login.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户登录</title>
</head>
<body>
<form action="" method="post">{{form.user}} {{form.user.errors[0]}}{{form.pwd}} {{form.pwd.errors[0]}}<input type="submit" value="提交">{{msg}}
</form>
</body>
</html>

用户注册验证:

  • 注册页面需要让用户输入:用户名、密码、确认密码、性别、爱好…

来一个实战例子,底下会拆分详讲:

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core, simple, html5
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass RegisterForm(Form):name = simple.StringField(label='用户名',validators=[validators.DataRequired()],widget=widgets.TextInput(),render_kw={'class': 'form-control'},default='GuHanZhe'                         # 页面输入框默认值)pwd = simple.PasswordField(label='密码',validators=[validators.DataRequired(message='密码不能为空')],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})pwd_confirm = simple.PasswordField(label='确认密码',validators=[validators.DataRequired(message='确认密码不能为空'),validators.EqualTo('pwd', message='两次密码输入不一致')    # EqualTo作用是比较当前字段和指定字段名的字段值是否相等],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})email = html5.EmailField(label='邮箱',validators=[validators.DataRequired(message='邮箱不能为空'),validators.Email(message='邮箱格式有误')],widget=widgets.TextInput(input_type='email'),render_kw={'class': 'form-control'})gender = core.RadioField(label='性别',choices=((1, '男'),(2, '女'),),coerce=int)city = core.SelectField(label='城市',choices=(('bj', '北京'),('sh', '上海')))hobby = core.SelectMultipleField(label='爱好',choices=((1, '篮球'),(2, '足球')),coerce=int)favor = core.SelectMultipleField(label='爱好',choices=((1, '篮球'),(2, '足球')),widget=widgets.ListWidget(prefix_label=False),option_widget=widgets.CheckboxInput(),coerce=int,default=[1, 2])def __int__(self, *args, **kwargs):super(RegisterForm, self).__init__(*args, **kwargs)self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))def validate_pwd_confirm(self, field):"""自定义pwd_confirm字段规则,例:与pwd字段是否一致"""# 最开始初始化时,self.data中已有所有值if field.data != self.data['pwd']:# raise validators.ValidationError('密码不一致')   # 继续后续字段的验证raise validators.StopValidation('密码不一致')      # 不再继续后续字段的验证@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'GET':form = RegisterForm(data={'gender': 1})return render_template('register.html', form=form)form = RegisterForm(formdata=request.form)if form.validate():print('用户提交数据通过格式验证,提交的值为:', form.data)else:print(form.errors)return render_template('register.html', form=form)if __name__ == '__main__':app.run()
  1. 有关于上述代码中coerce=int的作用:

    在这里插入图片描述

    wtforms库中,coerce参数是用来强制转换字段值的参数。coerce=int的作用是将选项中的值强制转换为整数类型

    RadioField中,choices参数定义了可选的值,它是一个元组,其中包含了每个选项的值和标签。在上图中,每个选项的值是1和2,而标签是’男’和’女’。由于HTTP表单提交的数据通常是字符串形式,使用coerce=int告诉wtforms将用户提交的值强制转换为整数类型。

    这对于确保表单数据的类型与后端处理代码的期望类型一致非常有用。在这个例子中,gender字段的值将被强制转换为整数,而不是保持为字符串。这样,在处理表单数据时就可以直接使用整数类型,而不需要手动进行类型转换。

  2. 上述我定义了这么多的字段,难道在写前端register.html代码的时候要一个个敲吗???

    肯定不是的!

    wtforms支持我们使用for循环~

    回想一下:一个类的实例如何才能支持for循环?

    在《Python全栈系列教程》专栏里讲过,只要一个类内部实现了iter魔法方法,且这个方法返回了一个迭代器,那么这个类的实例就支持for循环。

    在这里插入图片描述

    所以来看下wtforms源码,确认一下:

    进Form —> 进BaseForm:

    在这里插入图片描述

使用示例:

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core, simple, html5
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass RegisterForm(Form):name = simple.StringField(label='用户名',validators=[validators.DataRequired()],widget=widgets.TextInput(),render_kw={'class': 'form-control'},default='GuHanZhe'                         # 页面输入框默认值)pwd = simple.PasswordField(label='密码',validators=[validators.DataRequired(message='密码不能为空')],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})pwd_confirm = simple.PasswordField(label='确认密码',validators=[validators.DataRequired(message='确认密码不能为空'),validators.EqualTo('pwd', message='两次密码输入不一致')    # EqualTo作用是比较当前字段和指定字段名的字段值是否相等],widget=widgets.PasswordInput(),render_kw={'class': 'form-control'})email = html5.EmailField(label='邮箱',validators=[validators.DataRequired(message='邮箱不能为空'),validators.Email(message='邮箱格式有误')],widget=widgets.TextInput(input_type='email'),render_kw={'class': 'form-control'})gender = core.RadioField(label='性别',choices=((1, '男'),(2, '女'),),coerce=int)city = core.SelectField(label='城市',choices=(('bj', '北京'),('sh', '上海')))hobby = core.SelectMultipleField(label='爱好',choices=((1, '篮球'),(2, '足球')),coerce=int)favor = core.SelectMultipleField(label='爱好',choices=((1, '篮球'),(2, '足球')),widget=widgets.ListWidget(prefix_label=False),option_widget=widgets.CheckboxInput(),coerce=int,default=[1, 2])@app.route('/register', methods=['GET', 'POST'])
def register():if request.method == 'GET':form = RegisterForm()return render_template('register.html', form=form)form = RegisterForm(formdata=request.form)if form.validate():print('用户提交数据通过格式验证,提交的值为:', form.data)else:print(form.errors)return '登录成功~'if __name__ == '__main__':app.run()

register.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>用户注册</title>
</head>
<body>
<form action="" method="post">{% for item in form %}<p>{{ item.label }}: {{item}} {{item.errors[0]}}</p>{% endfor %}<input type="submit" value="提交">
</form>
</body>
</html>
  • 问题引入:

    实际生产中,可能有些下拉框的值是从数据库中取出展示的。此处以city这个为例:

        city = core.SelectField(label='城市',choices=SQLHelper.fetch_all('select id, name from city_info', {}))
    

    如果直接运行访问,这个下拉框是没有任何问题的。

    但是实际生产中可能会遇到的一个问题是:Flask服务没关,但是往数据库这张表加了几条数据,那么,不管怎样刷新页面,这个下拉框都不会出现这些新加的数据。

    但是将Flask服务重启一下就OK 了。

    原因很简单——因为在RegisterForm类中这些字段都是静态字段,运行的时候只执行一次!

  • 解决方法就是:

    重写RegisterForm类的构造方法:让每次实例化这个类的时候都执行一次sql查询语句并更新对应字段值:

        def __int__(self, *args, **kwargs):super(RegisterForm, self).__init__(*args, **kwargs)self.city.choices = SQLHelper.fetch_all('select id, name from city_info', {})

如何自定义校验规则:

【方法名以validate_开头,后面是对应需要校验的字段名】

    def validate_pwd_confirm(self, field):"""自定义pwd_confirm字段规则,例:与pwd字段是否一致"""# 最开始初始化时,self.data中已有所有值# field.data就是当前字段的值if field.data != self.data['pwd']:# raise validators.ValidationError('密码不一致')   # 继续后续字段的验证raise validators.StopValidation('密码不一致')      # 不再继续后续字段的验证

抽象解读使用wtforms编写的类:

简单谈一嘴:

比如LoginForm类中的user字段,很容易知道user是一个实例(可以点进去StringField发现它是一个类):

在这里插入图片描述

在视图函数中,实例化form后将其传给了前端:

在这里插入图片描述

而前端就相当于执行了print(form.user)

那么这就执行对应类StringField的str魔法方法,这个玩意返回什么,页面就看到什么~

开始抽象:

class LoginForm(Form):user = 类(正则, 插件)字段 = 类(正则, 插件)字段 = 类(正则, 插件)字段 = 类(正则, 插件)form = LoginForm(Form)
# 生成html标签
print(form.user)   ——>   类.__str__   ——>   插件.xx方法# 验证
form = LoginForm(formdata=request.form)
if form.validate():# 内部找到所有的字段:#                  比如:user + 用户发过来的对应的数据   ——>    正则校验

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/85.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 磁盘占用过多问题处理过程记录

一、问题描述 突然发现服务器磁盘使用超过95%了&#xff08;截图时2.1 和 2.2 已经执行过了&#xff09; 二、问题分析与解决 2.1&#xff0c;docker 无用镜像占用磁盘 # 使用 docker images 查看服务的镜像 docker images# 可以手动删除一些很大不用的 docker rmi ***## 也…

一秒内传输50万对纠缠光子?!纽约市量子网络刷新纪录

量子网络技术行业的领军企业Qunnect宣布&#xff0c;在纽约市的GothamQ网络上&#xff0c;其偏振量子比特的传输性能刷新了纪录。Qunnect利用现有的商用光缆实现了每秒传输50万对高保真度纠缠光子的速率&#xff0c;且该网络的正常运行时间超过了99%。 纽约34公里长的GothamQ量…

服务器数据恢复—RAID5故障导致SAP+oracle数据丢失的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌服务器存储中有一组由6块SAS硬盘组建的RAID5阵列&#xff0c;其中有1块硬盘作为热备盘使用。上层划分若干lun&#xff0c;存放Oracle数据库数据。 服务器存储故障&分析&#xff1a; 该RAID5阵列中一块硬盘出现故障离线&#xff0…

开启Three.js之旅(会持续完善)

文章目录 Three.js必备构建项目场景Scene相机CameraPerspectiveCamera 渲染器WebGLRendererCSS3DRenderer 灯光LightAmbientLightDirectionalLight 平行光PointLight 加载器CacheFileLoaderLoaderGLTFLoaderRGBELoaderTextureLoader 材质MetarialMeshBasicMaterialMeshLambertM…

k8s集群资源编排清单文件解读

1、YAML 文件概述 k8s集群中对资源管理和资源对象编排部署都可以通过声明样式&#xff08;YAML&#xff09;文件来解决&#xff0c;也就是可以把需要对资源对象操作编辑到 YAML 格式文件中&#xff0c;我们把这种文件叫做资源清单文件&#xff0c;通过 kubectl 命令直接使用资源…

原子的内部结构

原子非常神奇&#xff0c;花时间思考它是非常有价值的。尽管传统的太阳系示意图存在致命的缺点&#xff0c;但我们还是可以局部应用于原子。 首先&#xff0c;原子与太阳系具有相似性一原子的中心质量大&#xff0c;外部质量小。我们用最简单的氢原子做分析&#xff0c;氢原子…

辽宁梵宁教育课程概览:打造职场新人的设计技能利器

随着数字化时代的快速发展&#xff0c;设计技能在职场中的重要性日益凸显。对于职场新人而言&#xff0c;掌握优秀的设计能力不仅有助于个人职业发展&#xff0c;更能为企业创造更多价值。辽宁梵宁教育&#xff0c;作为一所致力于培养职场新人设计技能的培训机构&#xff0c;以…

决策树分类器(保姆级教学) 定义+特性+原理及公式+鸢尾花分类经典问题示例(完整Python代码带详细注释、保姆级分部代码解释及结果说明、决策树可视化及解释)

文章目录 引言定义特性基本原理和公式理解信息增益&#xff08;ID3算法&#xff09;熵的定义条件熵信息增益的计算 基尼不纯度&#xff08;CART算法&#xff09;基尼不纯度的定义基尼不纯度的计算例子 实现步骤解决鸢尾花分类问题&#xff08;机器学习入门中的经典案例Python代…

makefile第七讲

更多精彩内容在公众号。 当make执行完后&#xff0c;我们期望将最终的可执行文件安装到系统目录下&#xff0c;这样在不同的目录下都可以执行编译的可执行文件&#xff0c;相当于做成了个命令。这个就需要用到make install。 源文件如下&#xff1a;用于判断系统是小端还是大端…

性能分析与调优

性能分析方法 自底向上&#xff1a;通过监控硬件及操作系统性能指标&#xff08;cpu、内存、磁盘、网络等硬件资源的性能指标&#xff09;来分析性能问题&#xff08;配置、程序问题&#xff09; 先检查&#xff0c;再下药 自顶向下&#xff1a;通过生成负载来观察被测试的系…

【ROS2笔记七】ROS中的参数通信

7.ROS中的参数通信 文章目录 7.ROS中的参数通信7.1使用CLI工具调整参数7.2参数通信之rclcpp实现7.2.1创建节点7.2.2rclcpp参数API Reference ROS2中的参数是由键值对组成的&#xff0c;参数可以实现动态调整。 7.1使用CLI工具调整参数 启动turtlesim功能包的环境 ros2 run …

如何在本地创建一个贪吃蛇小游戏node.js服务并实现无公网IP远程游玩

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽…

OSPF笔记+大实验

OSPF综合大实验---实验报告 配置IP地址 R1&#xff1a; [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 172.16.33.1 24 [R1-GigabitEthernet0/0/0]int l0 [R1-LoopBack0]ip add 172.168.34.1 24 R2: [R2]int g0/0/0 [R2-GigabitEthernet0/0/0]ip add 172.16.33.2 24…

Jmeter接口测试:使用教程(下)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号&#xff1a;互联网杂货铺&#xff0c;回复1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 上一篇我给大家讲了jmeter的基本介绍跟参数化和jmeter脚…

【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现

前言 上文说到&#xff0c;Spring Security它是一个强大的和高度可定制的身份验证和访问控制框架。它提供了一套丰富的功能&#xff0c;用于保护基于Spring的应用程序。 上文又说到&#xff0c;在Spring Security中&#xff0c;过滤器&#xff08;Filter&#xff09;是一个重…

png静图转换gif动图如何操作?轻松一键快速转换gif动图

想要把多张Png格式图片转换成gif格式动图时要怎么操作&#xff1f;图片常见的有静图和动图&#xff0c;而jpg、png、gif等是最常见的图片格式。想要把png格式图片转换成gif动画还不想下载任何软件的时候就可以使用gif制作工具。不需要下载软件在线就能操作。能够轻轻松松就能快…

北斗卫星系统在海上测量中的创新应用

北斗卫星系统在海上测量中的创新应用 随着全球导航卫星系统技术的飞速发展&#xff0c;北斗卫星系统作为中国自主研发的全球卫星导航系统&#xff0c;在海上测量和导航领域展现出了无可比拟的优势和广阔的应用前景。 一、北斗卫星系统概述 北斗卫星系统是由中国自主研发的全球…

idea中打印日志不会乱码,但是部署到外部tomcat中乱码了。

问题&#xff1a;如图Tomcat乱码&#xff0c;而且启动时的系统日志不会乱码&#xff0c;webapp中的打印日志才乱码。 idea中的情况如下&#xff1a;正常中文展示。 问题分析&#xff1a;网上分析的原因是Tomcat配置的字符集和web应用的字符集不匹配&#xff0c;网上集中的解决…

Unity类银河恶魔城学习记录12-11 P133 Merge Skill Tree with Parry skill源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Parry_Skill.cs using UnityEngine; using UnityEngine.UI;public class P…

MySQL进阶 ==> 引擎选择优化指南

数据库引擎的选择&#xff1a; InnoDB InnoDB存储引擎是Mysql的默认存储引擎。InnoDB存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎&#xff0c;InnoDB写的处理效率差一些&#xff0c;并且会占用更多的磁盘空间以保留数据和索引。 存储方…