flask--Wtform

栏目: Python · 发布时间: 7年前

内容简介: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

flask--Wtform

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

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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Smashing Book

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》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具