内容简介:flask--Wtform
一、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__再到这个方法,原来不知道,后来通过看了看源码就了解了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Smashing Book
Jacob Gube、Dmitry Fadeev、Chris Spooner、Darius A Monsef IV、Alessandro Cattaneo、Steven Snell、David Leggett、Andrew Maier、Kayla Knight、Yves Peters、René Schmidt、Smashing Magazine editorial team、Vitaly Friedman、Sven Lennartz / 2009 / $ 29.90 / € 23.90
The Smashing Book is a printed book about best practices in modern Web design. The book shares technical tips and best practices on coding, usability and optimization and explores how to create succes......一起来看看 《The Smashing Book》 这本书的介绍吧!