简介

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

安装:pip3 install wtforms

用户登录注册示例

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

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

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

实例下载:https://files.cnblogs.com/files/wupeiqi/flask-wtforms-example.zip

3. meta

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 md5 app = Flask(__name__, template_folder='templates')
app.debug = True class MyCSRF(CSRF):
"""
Generate a CSRF token based on the user's IP. I am probably not very
secure, so don't use me.
""" def setup_form(self, form):
self.csrf_context = form.meta.csrf_context()
self.csrf_secret = form.meta.csrf_secret
return super(MyCSRF, self).setup_form(form) def generate_csrf_token(self, csrf_token):
gid = self.csrf_secret + self.csrf_context
token = md5(gid.encode('utf-8')).hexdigest()
return token def 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标签name
csrf_field_name = 'csrf_token' # 自动生成标签的值,加密用的csrf_secret
csrf_secret = 'xxxxxx'
# 自动生成标签的值,加密用的csrf_context
csrf_context = lambda x: request.url
# 生成和比较csrf标签
csrf_class = MyCSRF # -- i18n
# 是否支持本地化
# locales = False
locales = ('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 obj class Foo(object,metaclass=MyType):
user = 'wupeiqi'
age = 18 obj = 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 obj class Foo(MyType('MyType', (object,), {})):
user = 'wupeiqi'
age = 18 obj = 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 obj def with_metaclass(arg,base):
return MyType('MyType', (base,), {}) class Foo(with_metaclass(MyType,object)):
user = 'wupeiqi'
age = 18 obj = 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.default self.object_data = data try:
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 = None
success = True
for 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 = False
return success
c. 每个字段进行验证时候
字段的pre_validate 【预留的扩展】
字段的_run_validation_chain,对正则和字段的钩子函数进行校验
字段的post_validate【预留的扩展】

参考:http://www.cnblogs.com/wupeiqi/articles/8202357.html

Flask--WTForms的更多相关文章

  1. flask wtforms组件详解

    一.简介 在flask内部并没有提供全面的表单验证,所以当我们不借助第三方插件来处理时候代码会显得混乱,而官方推荐的一个表单验证插件就是wtforms.wtfroms是一个支持多种web框架的form ...

  2. Flask WTForms的使用和源码分析 —— (7)

    Flask-WTF是简化了WTForms操作的一个第三方库.WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板.还有其它一些功能:CSRF保护, 文件上传等.安装方法: pip3 ...

  3. Flask wtforms实现简单的登录注册

    目录结构 视图 # -*- coding:utf-8 -*- # Author : Niuli # Data : 2019-02-27 19:26 from flask import render_t ...

  4. WTForms In Flask(WTForms在Flask中的应用)

    WTForms WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 安装wtforms : pip3/pip install wtforms 用户登录/注册示例 项 ...

  5. Flask wtforms 表单验证使用

    目录 wtforms 使用1(简单版): 使用2(复杂版): wtforms 安装:pip3 install wtforms 使用1(简单版): from flask import Flask, re ...

  6. flask wtforms 的效验

    flask版 .py from flask import Flask, render_template, request, session, current_app, g, redirect from ...

  7. flask WTForms源码分析及自定义WTForms

    首先我们来创建一个From类 from wtforms.form import Form from wtforms import StringField from wtforms.validators ...

  8. Flask构建微电影(一)

    第一章.项目介绍  1.1.前言    本教程我将带领大家如何使用flask框架开发微电影网站.Flask是python中最受欢迎的轻量级web框架,flask扩展丰富,冗余度小,可自由选择组合各种插 ...

  9. Flask开发微电影网站(一)

    1.用到的Flask知识 1.使用整形,浮点型,路径型,字符串型下正则表达式路由转化器 2.使用GET与POST请求,上传文件,cookie获取与响应,404处理 3.使用模板自动转义,定义过滤器,定 ...

  10. Flask 构建微电影视频网站(四)

    后台管理 实现后台管理系统使用flask sqlalchemy结合mysql数据库进行增删改查操作.分页的使用.路由装饰器定义.模板中变量调用.登录会话机制.上传文件.flask wtforms表单使 ...

随机推荐

  1. [LeetCode&Python] Problem 27. Remove Element

    Given an array nums and a value val, remove all instances of that value in-placeand return the new l ...

  2. docker-compose部署mongodb+redis遇到的问题

    Demo环境下需要用到Redis+mongodb两种DB配合使用,所以暂时直接使用docker的redids和mongodb镜像,用docker-compose进行联合部署 使用的版本如下: dock ...

  3. Python全栈之路----常用模块----hashlib加密模块

    加密算法介绍 HASH       Python全栈之路----hash函数 Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列 ...

  4. 前端-----js

    一 补充css的层叠性 权重: 行内样式(1000)>内接样式(如link等...) id(100)>class(010)>标签(001) 如果同是继承来的,距目标越近的优先级越高 ...

  5. Byte数组和字符串相互转换的问题

    第一:需求:将文件转成byte数组,之后转成字符串返回.过滤器接收到响应内容后,需要将响应的内容转成byte数组. 第二:我刚开始的做法: Controller:byteArr = Conversio ...

  6. Python学习笔记,day5

    Python学习笔记,day5 一.time & datetime模块 import本质为将要导入的模块,先解释一遍 #_*_coding:utf-8_*_ __author__ = 'Ale ...

  7. 《Java核心技术(卷一)》读书笔记——第六章:内部类

    1.      内部类的概念? 类中类 2.      为什么要用内部类? 内部类的方法可以访问外部类的实例域 内部类对外部类的同一个包中的类实现了隐藏 匿名内部类在“想要定义一个回调函数却又不想编写 ...

  8. 作业二:构建swap函数

    一.swap代码 #include<stdio.h> int main() //主函数部分 { void swap(int *m,int *n); int a,b; int *p1,*p2 ...

  9. c——简单排序

    1.交换排序 a.冒泡排序 #include <stdio.h> int main() { , , , , }, i, j, t; ; i>=; i--) { ; j<i; j ...

  10. sublime An unhandled OS error was encountered nodejspath_error

    sublime An unhandled OS error was encountered  nodejspath_error 点击ok,修改node_path typescript 插件下载 ctr ...