一、Wtform

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

安装:

   pip3 install wtform

用途:

 1. 用户登录注册

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

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

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
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 widgets app = Flask(__name__, template_folder='templates')
app.debug = True class 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() app.py

app.py

<!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>

login

2.用户注册

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

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 widgets app = Flask(__name__, template_folder='templates')
app.debug = True class 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
)
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):
"""
自定义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})
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()

APP.py

<!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>

register.html

metaclass分析:
class MyType(type):
def __init__(self,*args,**kwargs):
print('xxxx')
super(MyType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs)
cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
return obj def with_metaclass(base):
return MyType("MyType",(base,),{}) class Foo(with_metaclass(object)):
def __init__(self,name):
self.name = name
class Form(with_metaclass(FormMeta, BaseForm)):   #相当于给这个form类定义了一个makeclass,这个类在刚创建时,先执行makeclass,这行makeclass的__init__方法
pass def with_metaclass(meta, base=object):
#meta=FormMeta(("NewBase", (BaseForm,), {})) #实例化了这个类
#base=BaseForm (base继承了BaseForm)
return meta("NewBase", (base,), {})
class LoginForm(Form):    #当执行到这一句是,他在formdata这个类中,LoginForm这个类中 里边有(LoginForm._unbound_fields = None
LoginForm._wtforms_meta = None这两个字段)
name=simple.StringField( #name实例化一个StringField(如果这个name中有makeclass这个类先要执行makeclass这个类,一个类在实例化__init__之前 有__new__,__call__,type,这几个方法,
对一个类来说整个请求进来先执行type的__init__方法,实例化的时候先执行type的__call__方法,有type的__call__方法调用类的 __new__方法,然后在执行类的__init__方法,这才叫实例化完成。对与这个StringField类方法,
如果有__new__方法,先执行__new__方法,点击进去找到它的new方法,最开始写的是StringField但正真的最开始是name=UnboundField(cls, *args, **kwargs)
(name对应的是UnboundField)在这个UnboundField中封装creation_counter,它就等于当前creation_counter自加1)
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空'),
validators.Length(min=5,max=15,message='用户名的长度必须大于5且小于15')
],
widget=widgets.TextInput(),
render_kw={'class':'form-control'}
)
pwd=simple.PasswordField( #(在执行这个是pwd=UnboundField(cls, *args, **kwargs)但是当他执行时UnboundField中的creation_counter这个静态字段已经被更新,
用creation_counter这个来计数是因为跟你以后在页面上显示的次序有关系)
label='密码',
validators=[
validators.DataRequired(message='密码不能为空'),
validators.Length(min=6,max=15,message='密码的长度必须大于6且小于15')
],
widget=widgets.TextInput(),
render_kw={'class':'form-control'}
)

3.Wtform源码分析

form类

from wtforms import Form

字段功能:(1.通过正则进行校验;2.插件生成HTML标签

from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple

插件

from wtforms import widgets

from wtforms import validators

源码:

    代码刚开始运行时:先执行LoginForm这个类的字段开始实例化

    LoginForm 继承From,这个类是有type创建 ,默认makeclass=type     (makeclass可以自定义)

流程:

from flask import Flask,request,render_template,redirect

from wtforms import Form
from wtforms import widgets
from wtforms import validators from wtforms.fields import core
from wtforms.fields import simple
from wtforms.fields import html5 app = Flask(__name__) class LoginForm(Form):
name=simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空'),
validators.Length(min=5,max=15,message='用户名的长度必须大于5且小于15')
],
widget=widgets.TextInput(),
render_kw={'class':'form-control'}
)
pwd=simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空'),
validators.Length(min=6,max=15,message='密码的长度必须大于6且小于15')
],
widget=widgets.TextInput(),
render_kw={'class':'form-control'}
) @app.route('/login',methods=['GET','POST'])
def login():
#程序刚进来是get请求,对
if request.method=='GET':
#程序刚进来是get请求先实例化form=LoginForm(),先执行type的__call__方法(有__call__就执行,没有就跳过不执行,它的内部执行了接下来执行LoginForm
的__new__方法,再走LoginForm de __init__方法) #页面标签数据初始化:data=字典,obj=对象.字段,formdata有getlist方法
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()
“先执行__call__方法” 

   def __call__(cls, *args, **kwargs):
"""
Construct a new `Form` instance. Creates the `_unbound_fields` list and the internal `_wtforms_meta`
subclass of the class Meta in order to allow a proper inheritance
hierarchy.
"""
if cls._unbound_fields is None:
fields = []
for name in dir(cls): #(cls是LoginForm这个类,dir把这个类的所有字段都拿到)
if not name.startswith('_'): #(判断如果以'_'开头)
#获取静态字段的值:Unbound_Field对象
unbound_field = getattr(cls, name) #(拿到Unbound_Field的对象)
if hasattr(unbound_field, '_formfield'):
fields.append((name, unbound_field))#(fields这个列表里面是一个元组,元组里面一个是它的名称,一个是它的Unbound_field对象,每个
Unbound_field对象有creation_counter用来计数)
# We keep the name as the second element of the sort 
# to ensure a stable sort.
fields.sort(key=lambda x: (x[1].creation_counter, x[0])) #优先按照Unbound_field的count数来排序 ,fieldsshi 排序过后的字段
cls._unbound_fields = fields
# Create a subclass of the 'class Meta' using all the ancestors.
if cls._wtforms_meta is None: bases = []
for mro_class in cls.__mro__: #找到所有的继承关系,相当于继承所有的类
if 'Meta' in mro_class.__dict__: #mro_class.dict__ 就是;类中的所有成员
bases.append(mro_class.Meta)
cls._wtforms_meta = type('Meta', tuple(bases), {}) #type('Meta')表示。自己创建一个Meta类继承bases
           return type.__call__(cls, *args, **kwargs)
'执行login的__new__方法,没有__new__方法执行它的__init__方法'

def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):

    meta_obj = self._wtforms_meta()   #原来创建的Meta类(实例化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):
# Set all the fields to attributes so that they obscure the class
# attributes with the same names.
setattr(self, name, field)
self.process(formdata, obj, data=data, **kwargs)

class BaseForm(object):
"""
Base Form Class. Provides core behaviour like field construction,
validation, and data and error proxying.
""" def __init__(self, fields, prefix='', meta=DefaultMeta()):
"""
:param fields:
A dict or sequence of 2-tuples of partially-constructed fields.
:param prefix:
If provided, all fields will have their name prefixed with the
value.
:param meta:
A meta instance which is used for configuration and customization
of WTForms behaviors.
"""
if prefix and prefix[-1] not in '-_;:/.':
prefix += '-' self.meta = meta
self._prefix = prefix
self._errors = None
self._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)
#对每一个UNbound中的字段进行实例化
field = meta.bind_field(self, unbound_field, options)
self._fields[name] = field
get执行完现在开始校验
def validate(self):
"""
Validates the form by calling `validate` on each field, passing any
extra `Form.validate_<fieldname>` validators to the field validator.
"""
extra = {}
for name in self._fields: #_fields是所有的字段
inline = getattr(self.__class__, 'validate_%s' % name, None)
if inline is not None:
extra[name] = [inline] return super(Form, self).validate(extra) #调用父类的validate

你觉得基础知识那些最重要函数也重要, 装饰器,闭包也是蛮重要的。mainxiang面向对象基础流程也是挺重要的,为什么,因为它的流程我知道是

通过流程type,__call__,__new__再到这个方法,原来不知道,后来通过看了看源码就了解了。

flask--Wtform的更多相关文章

  1. flask + wtform + google storage

    项目需要使用 flask 上传.下载文件到 google storage 上, 搜了一圈没有能直接结合 wtform 使用的插件,所以动手造了个轮子. 只实现了基本的上传,下载的功能,后续可能会完善预 ...

  2. Flask wtform组件

    Wtforms简介 WTForms是一个支持多个web框架的form组件 主要能够帮助我们生成html标签 对数据进行验证 安装 pip install wtforms Wtforms的使用 这里借助 ...

  3. Flask WTForm disable choice field

    Flask disable choice field ChoiceField = { render_kw={'disabled':''} } form.my_field.render_kw = {'d ...

  4. Python __call__详解

    可以调用的对象 关于 __call__ 方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数.内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都 ...

  5. 【Flask】 WTForm表单编程

    WTForm表单编程 在网页中,为了和用户进行信息交互总是不得不出现一些表单.flask设计了WTForm表单库来使flask可以更加简便地管理操作表单数据.WTForm中最重要的几个概念如下: Fo ...

  6. flask之wtform与flask-session组件

    1.wtform from flask import Flask, render_template, request, redirect from wtforms import Form from w ...

  7. [Python][flask][flask-wtf]关于flask-wtf中API使用实例教程

    简介:简单的集成flask,WTForms,包括跨站请求伪造(CSRF),文件上传和验证码. 一.安装(Install) 此文仍然是Windows操作系统下的教程,但是和linux操作系统下的运行环境 ...

  8. Flask 学习 十 博客文章

    提交和显示博客文章 app/models.py 文章模型 class Post(db.Model): __tablename__ = 'posts' id = db.Column(db.Integer ...

  9. python flask框架 tempates 模版的使用

    在py文件同级下 建立templates文件夹,再文件夹中编写html文件 1 向模版中传递参数: ''' 1 向模板传送 参数 ''' @app.route('/') def index(): na ...

  10. Flask 源码流程,上下文管理

    源码流程 创建对象 from flask import Flask """ 1 实例化对象 app """ app = Flask(__na ...

随机推荐

  1. Java格式化日期的三种方式

    1)借助DateFormat类: public String toString(Date d) { SimpleDateFormat sdf = new SimpleDateFormat(“yyyy- ...

  2. jQuery 属性操作

    1 css操作 2 文本操作 3 属性操作 4 位置 5 尺寸 1.css操作 addClass();// 添加指定的CSS类名. removeClass();// 移除指定的CSS类名. hasCl ...

  3. Oracle 报 ORA-21561: OID generation failed 错误 mac 链接oracle

    简单的说:在终端输入:sudo scutil --set HostName localhost

  4. Joiner

    Joiner 我们经常需要将几个字符串,或者字符串数组.列表之类的东西,拼接成一个以指定符号分隔各个元素的字符串,比如把 [1, 2, 3] 拼接成 "1 2 3". 在 Pyth ...

  5. jqprint 打印网页 jQuery print plugin

    ref://jQuery print plugin <!DOCTYPE html> <html lang="en"> <script src=&quo ...

  6. Spring IOC/DI和AOP原理(转发:https://www.cnblogs.com/techroad4ca/p/5770073.html)

    一 IOC/DI 1. 概念及原理 IOC: Inversion of Control(控制反转)是一种设计思想,就是容器控制应用程序所需要外部资源的创建和管理,然后将其反转给应用程序.对象及其依赖对 ...

  7. Way to OpenSource

    @1:Follow any instructions in the readme and make sure that you can configure and run the project on ...

  8. LeetCode:搜索旋转排序数组【33】

    LeetCode:搜索旋转排序数组[33] 题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2]  ...

  9. 反射,hashlib模块,正则匹配,冒泡,选择,插入排序

    一.反射(自省) 首先通过一个例子来看一下本文中可能用到的对象和相关概念. import sys # 模块,sys指向这个模块对象import inspectdef foo(): pass # 函数, ...

  10. DevOps能力是落地微服务的前提

    在软件开发领域不存在银弹,当用一项新的技术或新的架构时一定要明白其背后的原理,确保把合适的技术应用在合适的项目上,而不是盲目跟风. 单体应用伸缩性差,而且随着应用规模的扩大,业务逻辑和开发部署过程都变 ...