Flask 第三方组件之 WTForms

简介

WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证。
安装: pip3 install wtforms

用户登录注册示例

1. 用户登录

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

  • 用户不能为空;用户长度必须大于6;
  • 密码不能为空;密码长度必须大于12;密码必须包含 字母、数字、特殊字符等(自定义正则);
############################## app.py #############################
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core    # 字段大部分都在core html5 simple 中
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgetsapp = Flask(__name__, template_folder='templates')
app.debug = Trueclass LoginForm(Form):name = simple.StringField(label='用户名',     # 显示名称validators=[       # 校验validators.DataRequired(message='用户名不能为空.'),validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')],widget=widgets.TextInput(),           # 插件render_kw={'class': 'form-control'}   # 渲染)pwd = simple.PasswordField(label='密码',validators=[validators.DataRequired(message='密码不能为空.'),validators.Length(min=8, message='用户名长度必须大于%(min)d'),validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')],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)else:form = LoginForm(formdata=request.form)           # 校验if form.validate():print('用户提交数据通过格式验证,提交的值为:', form.data)else:print(form.errors)return render_template('login.html', form=form)if __name__ == '__main__':app.run()
############################## login.html #############################
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post"><!--<input type="text" name="name">--><p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p><!--<input type="password" name="pwd">--><p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p><input type="submit" value="提交">
</form>
</body>
</html>

2. 用户注册

注册页面需要让用户输入:用户名、密码、密码重复、性别、爱好等。

############################## app.py #############################
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
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='alex')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="两次密码输入不一致")],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   # 会自动转化为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 __init__(self, *args, **kwargs):super(RegisterForm, self).__init__(*args, **kwargs)self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))def validate_pwd_confirm(self, field):  # 钩子函数,filed 代表要检验的字段,self 代表所有的数据"""自定义pwd_confirm字段规则,例:与pwd字段是否一致:param field: :return: """# 最开始初始化时,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})   # data 为form 传值return render_template('register.html', form=form)else: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()
############################## login.html #############################
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0  50px">{% for item in form %}<p>{{item.label}}: {{item}} {{item.errors[0] }}</p>{% endfor %}<input type="submit" value="提交">
</form>
</body>
</html>

3. meta

使用WTFrom 提供的 csrf_token,一般不怎么用

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect, session
from wtforms import Form
from wtforms.csrf.core import CSRF
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
from hashlib import md5app = Flask(__name__, template_folder='templates')
app.debug = Trueclass MyCSRF(CSRF):  # 继承CSRF,实现自己的规则"""Generate a CSRF token based on the user's IP. I am probably not verysecure, so don't use me."""def setup_form(self, form):self.csrf_context = form.meta.csrf_context()self.csrf_secret = form.meta.csrf_secretreturn super(MyCSRF, self).setup_form(form)def generate_csrf_token(self, csrf_token):gid = self.csrf_secret + self.csrf_contexttoken = md5(gid.encode('utf-8')).hexdigest()return tokendef validate_csrf_token(self, form, field):print(field.data, field.current_token)if field.data != field.current_token:raise ValueError('Invalid CSRF')class TestForm(Form):name = html5.EmailField(label='用户名')pwd = simple.StringField(label='密码')class Meta:# -- CSRF# 是否自动生成CSRF标签csrf = True# 生成CSRF标签namecsrf_field_name = 'csrf_token'# 自动生成标签的值,加密用的csrf_secretcsrf_secret = 'xxxxxx'# 自动生成标签的值,加密用的csrf_contextcsrf_context = lambda x: request.url# 生成和比较csrf标签csrf_class = MyCSRF# -- i18n# 是否支持本地化# locales = Falselocales = ('zh', 'en')# 是否对本地化进行缓存cache_translations = True# 保存本地化缓存信息的字段translations_cache = {}@app.route('/index/', methods=['GET', 'POST'])
def index():if request.method == 'GET':form = TestForm()else:form = TestForm(formdata=request.form)if form.validate():print(form)return render_template('index.html', form=form)if __name__ == '__main__':app.run()

其他:

1. metaclass

第一阶段

class MyType(type):def __init__(self, *args, **kwargs):print('MyType创建类',self)super(MyType, self).__init__(*args, **kwargs)def __call__(self, *args, **kwargs):obj = super(MyType, self).__call__(*args, **kwargs)print('类创建对象', self, obj)return objclass Foo(object,metaclass=MyType):user = 'flepeng'age = 18obj = Foo()

第二阶段

class MyType(type):def __init__(self, *args, **kwargs):super(MyType, self).__init__(*args, **kwargs)def __call__(cls, *args, **kwargs):v = dir(cls)obj = super(MyType, cls).__call__(*args, **kwargs)return objclass Foo(MyType('MyType', (object,), {})):user = 'wupeiqi'age = 18obj = Foo()

第三阶段

class MyType(type):def __init__(self, *args, **kwargs):super(MyType, self).__init__(*args, **kwargs)def __call__(cls, *args, **kwargs):v = dir(cls)obj = super(MyType, cls).__call__(*args, **kwargs)return objdef with_metaclass(arg,base):return MyType('MyType', (base,), {})class Foo(with_metaclass(MyType,object)):user = 'wupeiqi'age = 18obj = Foo()

2. 实例化流程分析

# 源码流程1. 执行type的 __call__ 方法,读取字段到静态字段 cls._unbound_fields 中; meta类读取到cls._wtforms_meta中2. 执行构造方法a. 循环cls._unbound_fields中的字段,并执行字段的bind方法,然后将返回值添加到 self._fields[name] 中。即:_fields = {name: wtforms.fields.core.StringField(),}PS:由于字段中的__new__方法,实例化时:name = simple.StringField(label='用户名'),创建的是UnboundField(cls, *args, **kwargs),当执行完bind之后,才变成执行 wtforms.fields.core.StringField()b. 循环_fields,为对象设置属性for name, field in iteritems(self._fields):# Set all the fields to attributes so that they obscure the class# attributes with the same names.setattr(self, name, field)c. 执行process,为字段设置默认值:self.process(formdata, obj, data=data, **kwargs)优先级:obj,data,formdata;再循环执行每个字段的process方法,为每个字段设置值:for name, field, in iteritems(self._fields):if obj is not None and hasattr(obj, name):field.process(formdata, getattr(obj, name))elif name in kwargs:field.process(formdata, kwargs[name])else:field.process(formdata)执行每个字段的process方法,为字段的data和字段的raw_data赋值def process(self, formdata, data=unset_value):self.process_errors = []if data is unset_value:try:data = self.default()except TypeError:data = self.defaultself.object_data = datatry:self.process_data(data)except ValueError as e:self.process_errors.append(e.args[0])if formdata:try:if self.name in formdata:self.raw_data = formdata.getlist(self.name)else:self.raw_data = []self.process_formdata(self.raw_data)except ValueError as e:self.process_errors.append(e.args[0])try:for filter in self.filters:self.data = filter(self.data)except ValueError as e:self.process_errors.append(e.args[0])d. 页面上执行print(form.name) 时,打印标签因为执行了:字段的 __str__ 方法字符的 __call__ 方法self.meta.render_field(self, kwargs)def render_field(self, field, render_kw):other_kw = getattr(field, 'render_kw', None)if other_kw is not None:render_kw = dict(other_kw, **render_kw)return field.widget(field, **render_kw)执行字段的插件对象的 __call__ 方法,返回标签字符串

3. 验证流程分析

a. 执行form的validate方法,获取钩子方法def validate(self):extra = {}for name in self._fields:inline = getattr(self.__class__, 'validate_%s' % name, None)if inline is not None:extra[name] = [inline]return super(Form, self).validate(extra)b. 循环每一个字段,执行字段的 validate 方法进行校验(参数传递了钩子函数)def validate(self, extra_validators=None):self._errors = Nonesuccess = Truefor name, field in iteritems(self._fields):if extra_validators is not None and name in extra_validators:extra = extra_validators[name]else:extra = tuple()if not field.validate(self, extra):success = Falsereturn successc. 每个字段进行验证时候字段的pre_validate 【预留的扩展】字段的_run_validation_chain,对正则和字段的钩子函数进行校验字段的post_validate【预留的扩展】
################################## class FormMeta ###############################
class FormMeta(type):# 创建类时,调用的方法def __init__(cls, name, bases, attrs):type.__init__(cls, name, bases, attrs)cls._unbound_fields = Nonecls._wtforms_meta = None# 实例化类时调用的方法def __call__(cls, *args, **kwargs):if cls._unbound_fields is None:fields = []for name in dir(cls):if not name.startswith('_'):unbound_field = getattr(cls, name)if hasattr(unbound_field, '_formfield'):   # 只要是继承自File,_formfield = Truefields.append((name, unbound_field))   # 将字段添加至fields fields.sort(key=lambda x: (x[1].creation_counter, x[0]))cls._unbound_fields = fields                       # _unbound_fields =[字段对应的示例,]if cls._wtforms_meta is None:bases = []for mro_class in cls.__mro__:if 'Meta' in mro_class.__dict__:bases.append(mro_class.Meta)               # 将所有的Meta添加至bases中cls._wtforms_meta = type('Meta', tuple(bases), {}) # 创建一个新的Meta,并继承自找到的Meta列表return type.__call__(cls, *args, **kwargs)             # 执行type的call方法,前面的都是派生的
################################## class Form ###############################
# class Form(BaseForm , classmate=FormMeta)
class Form(with_metaclass(FormMeta, BaseForm)):Meta = DefaultMetadef __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):meta_obj = self._wtforms_meta()if meta is not None and isinstance(meta, dict):meta_obj.update_values(meta)super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)for name, field in iteritems(self._fields):setattr(self, name, field)self.process(formdata, obj, data=data, **kwargs)def __setitem__(self, name, value):raise TypeError('Fields may not be added to Form instances, only classes.')def __delitem__(self, name):del self._fields[name]setattr(self, name, None)def __delattr__(self, name):if name in self._fields:self.__delitem__(name)else:unbound_field = getattr(self.__class__, name, None)if unbound_field is not None and hasattr(unbound_field, '_formfield'):setattr(self, name, None)else:super(Form, self).__delattr__(name)def validate(self):extra = {}for name in self._fields:inline = getattr(self.__class__, 'validate_%s' % name, None)if inline is not None:extra[name] = [inline]return super(Form, self).validate(extra)
################################## class BaseForm ###############################
class BaseForm(object):def __init__(self, fields, prefix='', meta=DefaultMeta()):if prefix and prefix[-1] not in '-_;:/.':prefix += '-'self.meta = metaself._prefix = prefixself._errors = Noneself._fields = OrderedDict()if hasattr(fields, 'items'):fields = fields.items()translations = self._get_translations()extra_fields = []if meta.csrf:self._csrf = meta.build_csrf(self)extra_fields.extend(self._csrf.setup_form(self))for name, unbound_field in itertools.chain(fields, extra_fields):options = dict(name=name, prefix=prefix, translations=translations)field = meta.bind_field(self, unbound_field, options)self._fields[name] = field

 

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

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

相关文章

机器学习原理与算法(六) 支持向量机

版权声明&#xff1a;本系列文章为博主原创文章&#xff0c;转载请注明出处&#xff01;谢谢&#xff01; 本章索引&#xff1a; 从第3章的Logistic回归算法开始&#xff0c;我们一直在讨论分类问题。在各种不同的分类算法中&#xff0c;...&#xff0c;我们一直在讨论如何分类…

读《程序员的SQL金典》[2]--函数

一、数学函数 1.RAND SELECT RAND () ---0.302870228294199取0-1之间的随机小数。 2.小数取整 CEILINT(data)舍掉小数部分并向上取整。FLOOR(data)舍掉小数部分并向下取整。SELECT TOP 3 FWeight, CEILING(FWeight ),FLOOR( FWeight) FROM T_PersonRound(m,d)&#xff1a;四舍…

html div模块前留空白,html – 3个DIV彼此相邻,中间填充空白

您好我想问你如何将3 DIV放在一起,而中间一个填补第一和第三DIV之间的空白.我想在第一个NAD第三个DIV中有动态按钮,我需要中间DIV来填充第一和第三个DIV之间的空间.我会破坏纯CSS / HTML(没有JavaScript)这是我的尝试&#xff1a;http://jsfiddle.net/4smx3627/#wrapper{height…

mplayer安装记录 源码分析

mplayer源码下载地址&#xff1a; http://www.mplayerhq.hu/MPlayer/releases/ 下载最新的MPlayer-1.0rc4 #mkdir /usr/local/mplayer #mkdir /usr/local/codecs #cd MPlayer-1.0rc4 #./configure --prefix/usr/local/mplayer --codecsdir/usr/local/ codecs --langua…

python人脸识别代码百度ai_python百度AI人脸识别API测试

1、注册账号 2、创建应用 3、得到AK和SK 4、用AK SK获取access_token 可用下面的代码&#xff1a; #!/usr/bin/python3.5 # encoding:utf-8 import requests # client_id 你的AK client_secret 你的SK host https://aip.baidubce.com/oauth/2.0/token?grant_typeclient_crede…

Flask 第三方组件之 SQLAlchemy

一、介绍 SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上&#xff0c;使用关系对象映射进行数据库操作&#xff0c;简言之便是&#xff1a;将类和对象转换成SQL&#xff0c;然后使用数据API执行SQL并获取执行结果。 安装&#xff1a;pip3 install sqlalc…

httpservlet获取请求端IP地址

request.getRemoteAddr(); 转载于:https://www.cnblogs.com/panxuejun/p/7623850.html

html 中怎样显示enum,JavaScript如何枚举?

JavaScript中对象的属性分为两种&#xff1a;数据属性和访问器属性。然后根据具体的上下文环境的不同&#xff0c;又可以将属性分为&#xff1a;原型属性和实例属性。原型属性是定义在对象的原型(prototype)中的属性&#xff0c;而实例属性一方面来自构造的函数中&#xff0c;然…

iperf测试网卡性能

Iperf是一个网络性能测试工具。可以测试TCP和UDP带宽质量&#xff0c;可以测量最大TCP带宽&#xff0c;具有多种参数和UDP特性&#xff0c;可以报告带宽&#xff0c;延迟抖动和数据包丢失 因为产品上确定要要用的&#xff30;&#xff28;&#xff39;是千&#xff2d;的&a…

acrobat 控件可以发布吗_短视频可以同时在多个平台发布吗?

我们在做自媒体内容创业中&#xff0c;很多人都在做视频版块&#xff0c;那么一个短视频到底能不能多平台同时发布呢&#xff1f;那么今天&#xff0c;我来分享给大家&#xff0c;希望能够帮到你解决困惑。1.作品可以多平台分发&#xff1a;大家不确定是否能多平台分发&#xf…

红河学院计算机科学与技术,2016年红河学院计算机科学与技术专业最低分是多少?...

类似问题答案2016年厦门理工学院计算机类(含计算机科学与技术、网络工程、空间信息与专业最低分...学校 地 区 专业 年份 批次 类型 分数 厦门理工学院 福建 计算机类(含计算机科学与技术、网络工程、空间信息与 2016 一批 理科 491 学校 地 区 专业 年份 批次 类型 分数 厦门理…

Flask 第三方组件之 script

Flask Script扩展提供向Flask插入外部脚本的功能&#xff0c;包括运行一个开发用的服务器&#xff0c;一个定制的Python shell&#xff0c;设置数据库的脚本&#xff0c;cronjobs&#xff0c;及其他运行在web应用之外的命令行任务&#xff1b;使得脚本和系统分开&#xff1b; …

CentOS四种方法自建yum仓库

将ISO光盘镜像作为yum本地仓库&#xff08;适用于不能联外网的环境&#xff09;&#xff1a; 1、 禁用所有可用的yum仓库&#xff0c;为方便演示&#xff0c;直接全部删除&#xff1a; # cd /etc/yum.repos.d # ls # rm -rf * 2、 创建光盘挂载点&#xff0c;挂载光盘&#x…

python substr_python数据分析-数据对象(一)

Python基本数据类型一般分为&#xff1a;数字、字符串、列表、元组、字典、集合这六种基本数据类型。不可变&#xff08;3 个&#xff09;&#xff1a;Number&#xff08;数字&#xff09;、String&#xff08;字符串&#xff09;、Tuple&#xff08;元组&#xff09;&#xff…

VLC框架分析

功能部份: VLC媒体播放器的核心是libvlc &#xff0c;它提供了界面&#xff0c;应用处理功能&#xff0c;如播放列表管理&#xff0c;音频和视频解码和输出&#xff0c;线程系统。所有libvlc源文件设在的/src目录及其子目录&#xff1a; # config/ &#xff1a;从命令行和配置…

html表格里的超链接点不了,Excel如何添加和取消超链接 Excel超链接打不开是怎么回事...

很多用户在制作excel表格的时候都会添加一些超链接&#xff0c;在制作完成后发布到网页&#xff0c;阅读者可以通过超链接打开指引的网页或者文件&#xff0c;超链接对制作excel表格的用户有非常大的帮助&#xff0c;虽然添加超链接的步骤非常简单&#xff0c;不过还是有些exce…

yum 安装apache php mysql

安装&#xff1a; yum install -y httpd php 查看版本&#xff1a;、 rpm -qa httpd php httpd-2.2.15-54.el6.centos.x86_64 php-5.3.3-48.el6_8.x86_64 修改apache配置文件&#xff1a; vim /etc/httpd/conf/httpd.conf 在#ServerName www.example.com:80行下添加一行 Server…

Python 散点图线性拟合_机器学习之利用Python进行简单线性回归分析

前言&#xff1a;在利用机器学习方法进行数据分析时经常要了解变量的相关性&#xff0c;有时还需要对变量进行回归分析。本文首先对人工智能/机器学习/深度学习、相关分析/因果分析/回归分析等易混淆的概念进行区分&#xff0c;最后结合案例介绍如何利用Python进行简单线性回归…

Flask 第三方组件之 Migrate

flask-migrate是flask的一个扩展模块,主要是扩展数据库表结构的.类似于Django的python manage.py migrate 官方文档: http://flask-migrate.readthedocs.io/en/latest/ 安装 pip install flask-migrate 使用举例 from flask import Flask from flask_sqlalchemy import SQLA…

html section 布局,section标签的用法

标签的用法由于昨晚发了一篇文章http://www.zcool.com.cn/article/ZMzA3MzI.html&#xff0c;有一个网友评论问 的用法。所以现在举例来说明一下&#xff1a;html5引入了标签&#xff0c;用于描述文档的结构&#xff0c;它同标签的意思一样。但是在特定环境中&#xff0c;两者又…