WTForms

  WTForms基礎操做,可見騷師博客html

  WTForms主要是兩個功能:1.生成HTML標籤  2.對數據格式進行驗證  前端

  這裏主要帶你們去看下WTForms源碼,是怎麼個實現流程?html5

  首先看到下面的示例代碼,捋順一下類之間的關係python

#!/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()

   form類下,封裝了兩個field對象,分別爲name,pwd,每一個field對象下面又給widget封裝 了input對象正則表達式

  第一:生成 HTML標籤數據庫

  在前端執行{{form.name}}和後端print(form.name)的效果是同樣的,因此它會執行對象裏的__str__方法,form.name是field對象,也就是執行field對象下的__str__方法,好比StringField,它下面是沒有這方法的,可是父類中Field是有的flask

    def __str__(self):
        return self()

   代碼執行了return self(),也就會執行field類中的__call__方法後端

    def __call__(self, **kwargs):
        return self.meta.render_field(self, kwargs)

   而在render_field方法中最後又執行了field.widget(field, **render_kw),field.widget是StringField下面封裝的widgets.TextInput對象,因此它又會去調用TextInput類下的__call__方法數據結構

  在父類input類下,找到了生成html標籤的代碼app

    def __call__(self, field, **kwargs):
        kwargs.setdefault('id', field.id)
        kwargs.setdefault('type', self.input_type)
        if 'value' not in kwargs:
            kwargs['value'] = field._value()
        if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
            kwargs['required'] = True
        return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))

 

  第二:對數據進行驗證

  1.  獲取前端請求字段數據
  2. 循環每一個字段進行正則匹配進行驗證
class InputText(object):

    def __str__(self):
        return '<input type="text" />'

class InputEmail(object):

    def __str__(self):
        return '<input type="mail" />'


class StringField(object):
    def __init__(self,wg,reg):
        self.wg = wg
        self.reg = reg

    def __str__(self):
        return str(self.wg)

    def valid(self,val):
        import re
        return re.match(self.reg,val)

class LoginForm(object):
    xyy = StringField(wg=InputText(),reg='\d+')
    lw = StringField(wg=InputEmail(),reg='\w+')

    def __str__(self,form):
        self.form = "用戶發來的全部數據{xyy:'df',lw:'sdf'}"

    def validate(self):
        fields = {'xyy':self.xyy,'lw':self.lw}
        for name,field in fields.items():
            # name是 xyy、。lw
            # field:  StringField(wg=InputText(),reg='\d+') / StringField(wg=InputEmail(),reg='\w+')
             # 'df'
            field.valid(self.form[name])


#
# wp = LoginForm()
# print(wp.xyy)
# print(wp.lw)

wp = LoginForm(formdata=request.form)
wp.validate()

   從上面咱們抽離出來的簡單模型,看得出:各個類,分工仍是很明確的,input類負責生成標籤,而field類負責本身字段數據驗證,而form主要統一驗證,返回結果

 

  上面過程只是簡單分析,詳細分析,請收看下一集

  開始分析前,咱們須要瞭解幾個知識點

  • __new__ 用於生成對象
class Foo(object):

    def __init__(self):
        pass


    def __new__(cls, *args, **kwargs):
        """
        用於生成對象
        :param args:
        :param kwargs:
        :return:
        """

        return super(Foo,cls).__new__(cls, *args, **kwargs)
        # return "666"


obj = Foo()
print(obj)
  •  __dict__獲取全部成員
class Foo(object):
    AGE = 123
    def __init__(self, na):
        self.name = na

#獲取全部成員鍵值對
print(Foo.__dict__)
#獲取全部成員的鍵
print(dir(Foo))

obj = Foo('laoliu')
print(obj.__dict__)
  •  列表排序
v = [11,22,334,4,2]
v.sort()
print(v)


v1 = [
    (11,'alex1'),
    (2,'alex2'),
    (2,'alex3'),
    (7,'alex4'),
]

#以元組的第一個元素排序
v1.sort(key=lambda x:x[0])
print(v1)

#以元組的第一個元素排序,若是相同,則以第二個元素排序
v1.sort(key=lambda x:(x[0],x[1]))
print(v1)
  •  __mro__ 按深度優先的關係,找到全部的父類
class F4(object):
    pass

class F3(F4):
    pass

class F2_5(object):
    pass

class F2(F2_5):
    pass

class F1(F2,F3):
    pass

print(F1.__mro__)

   接下來就開始咱們快樂的源碼分析之旅吧,let's go

 

  仍是看到咱們的示例代碼中,代碼從上往下解釋class LoginForm(Form),會去執行建立LoginForm類裏的__init__方法,那麼建立LoginForm的類是哪一個呢,你從父類中看到這句Form(with_metaclass(FormMeta, BaseForm)),就應該很快就能肯定是FormMeta,並且FormMeta確實繼承type類,因此建立LoginForm類時會去執行FormMeta裏的__init__方法(若是這裏還不懂,能夠去看個人另外一篇博客)

    def __init__(cls, name, bases, attrs):
        type.__init__(cls, name, bases, attrs)
        cls._unbound_fields = None
        cls._wtforms_meta = None

 

   在__init__方法裏,給LoginForm類封裝了兩個字段_unbound_fields,_wtforms_meta,都爲None

  代碼繼續往下解釋,遇到LoginForm定義兩個靜態字段name,pwd,進行simple.StringField實例化操做,因此會去執行StringField的__init__方法,執行__init__方法前,它先執行__new__方法,StringField中沒有,父類Field中有

    def __new__(cls, *args, **kwargs):
        if '_form' in kwargs and '_name' in kwargs:
            return super(Field, cls).__new__(cls)
        else:
            return UnboundField(cls, *args, **kwargs)

   kwargs是 咱們傳入的參數,咱們並無傳入了_form和_name參數,因此它會走else分支,最終name,pwd被賦值UnboundField對象,在這個對象中,封裝了StringField類和咱們實例的參數,因此建立LoginForm類,會是這個樣子

print(LoginForm.__dict__)
LoginForm ={
	'__module__': '__main__', 
	'name': <1 UnboundField(StringField, (), {'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>, 
	'pwd': <2 UnboundField(PasswordField, (), {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
	'__doc__': None, 
	'_unbound_fields': None, 
	'_wtforms_meta': None
}

   你可能會想:怎麼不直接實例StringField,而是實例一個UnboundField對象呢?UnboundField可以幫助咱們排序,它裏面維護了一個計數器,你可能又想了 幹嗎要排序?你在前端渲染時,循環form時須要一直保持一個規定順序,而在後端,咱們在定義代碼,是從上往下定義字段的,而獲取時,使用字典方式獲取的,字典是無序的,爲了還保持咱們定義的順序,就用上面對象來作到這點

 

  到視圖函數中,實例LoginForm(),會執行下面幾個步驟

  1. 執行FormMeta的__call__方法
  2. 執行LoginForm的__new__方法
  3. 執行LoginForm的__init__方法

  首先看到__call__方法中的這段

        if cls._unbound_fields is None:  #此時還爲None,會走這個分支
            fields = []
            for name in dir(cls):  #循環 類的成員全部鍵
                if not name.startswith('_'):  #排除帶_成員,只剩name,pwd
                    unbound_field = getattr(cls, name)  #獲取name和pwd對應的UnboundField對象
                    if hasattr(unbound_field, '_formfield'):  #UnboundField類中_formfield默認爲True
                        fields.append((name, unbound_field))
             #進行計算器排序
            fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
            cls._unbound_fields = fields  #最終賦給LoginForm的_unbound_fields 字段

   因此,此時_unbound_fields的存儲結構爲:

[
    (name, UnboundField對象(計算器,StringField類,參數)),
    (pwd, UnboundField對象(計算器,PasswordField類,參數))    
]

  咱們在看到__call__方法裏的這段代碼

        if cls._wtforms_meta is None: #此時也是爲None
            bases = []
            for mro_class in cls.__mro__:  #獲取當前類的全部繼承類
#咱們寫類裏是沒有Meta這個字段的,可是繼承類Form中Meta = DefaultMeta if 'Meta' in mro_class.__dict__: bases.append(mro_class.Meta) #bases = [DefaultMeta]
       #實例Meta類,繼承DefaultMeta,並賦給LoginForm的_wtforms_meta cls._wtforms_meta = type('Meta', tuple(bases), {})

   執行完__call__方法後,此時LoginForm長這樣

        class Meta(DefaultMeta,):
            pass

        LoginForm ={
            '__module__': '__main__',
            'name': <2 UnboundField(StringField, (), {'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>,
            'pwd': <1 UnboundField(PasswordField, (), {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>,
            '__doc__': None,
            '_unbound_fields': [
                (name, UnboundField對象(1,simple.StringField,參數)),
                (pwd, UnboundField對象(2,simple.PasswordField,參數)),
            ],
            '_wtforms_meta': Meta

    }

 

  因爲LoginForm和父類中沒有__new__方法,沒有就不執行

   

  LoginForm中沒有定義__init__方法,找到父類中Form的__init__方法執行

   看到這段代碼,最後執行了又執行了父類BaseForm的__init__方法,並把_unbound_fields和meta_obj傳了進入

        #實例Meta對象(主要用於生成csrf隱藏標籤)
        meta_obj = self._wtforms_meta()
        if meta is not None and isinstance(meta, dict):
            meta_obj.update_values(meta)  #若是form裏有設置meta,更新到meta_obj
        #執行父類中的__init__方法
        #_unbound_fields =  [
        #        (name, UnboundField對象(1,simple.StringField,參數)),
        #        (pwd, UnboundField對象(2,simple.PasswordField,參數)),
        #    ],
        super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)

   BaseForm的__init__方法這段代碼,真正實例StringField對象和PasswordField是在這下面乾的

        if meta.csrf:  #若是有設置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):
            #name  -->  name,pwd
            #unbound_field --> UnboundField對象(1,simple.StringField,參數)
            #               UnboundField對象(2,simple.PasswordField,參數)
            options = dict(name=name, prefix=prefix, translations=translations)
            field = meta.bind_field(self, unbound_field, options)
            #實例化Field對象後,把LoginForm對象中_fields下每一個字段的UnboundField對象替換成Field對象
            self._fields[name] = field

   看到meta.bind_field,最終調用 了unbound_field.bind方法

    def bind(self, form, name, prefix='', translations=None, **kwargs):
        kw = dict(
            self.kwargs,
            _form=form,
            _prefix=prefix,
            _name=name,
            _translations=translations,
            **kwargs
        )
        #實例化Field對象,此時參數攜帶_form和_name,在__new__方法中直接實例Field對象,再也不是UnboundField對象
        return self.field_class(*self.args, **kw) 

   執行BaseForm的__init__方法後,此時LoginForm對象的存儲內容長這樣

    form = {
        _fields: {
                name: StringField對象(),
                pwd: PasswordField對象(),
            }
    
        }
    

   再看到Form.__init__下的另一段代碼

        for name, field in iteritems(self._fields):
            setattr(self, name, field)  #把每一個字段設置到對象中,方便支持obj.name,obj.pwd訪問
        #爲每一個字段標籤設置默認值和驗證的值
        self.process(formdata, obj, data=data, **kwargs)

   因此此時的數據結構爲

    form = {
        _fields: {
                name: StringField對象(),
                pwd: PasswordField對象(),
            }
        name:  StringField對象(widget=widgets.TextInput()),
        pwd:  PasswordField對象(widget=widgets.PasswordInput())
    
        }
    

   上面源碼分析也只在get請求下,LoginForm不傳值

 

  那若是走post,也是LoginForm傳值,它又會怎麼走呢?

  請求來的時候,它仍是要先走實例LoginForm類的流程,這和get請求裏的過程是同樣的,惟一不一樣的,在執行LoginForm父類__init__方法時,self.process(formdata, obj, data=data, **kwargs)這裏的formdata是有值的,而後看到process方法裏的這段代碼

        #循環全部的字段
        '''
                _fields: {
                name: StringField對象(),
                pwd: PasswordField對象(),
            }
        '''
        #formdata 前端傳來的值  相似於{'name':alex,'pwd':123}
        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:  #kwargs 和 obj此時都沒值,因此會走這個分支
                #field=StringField...
                #把前端的值 封裝在StringField對象裏,此時對象裏有 要驗證的值 和 正則
                field.process(formdata)

   大概都能猜到field.process(formdata)幹了一件什麼事,就把前端的值對應字段對象進行封裝

        try:
            self.process_data(data)  #進行字段賦值操做
        except ValueError as e:
            self.process_errors.append(e.args[0])

        if formdata is not None:
            if self.name in formdata:
                self.raw_data = formdata.getlist(self.name)
            else:
                self.raw_data = []

            try:
                self.process_formdata(self.raw_data)  #也是進行字段賦值操做
            except ValueError as e:
                self.process_errors.append(e.args[0])

   

  傳值form後,接下來的就是驗證了,猜它都會循環每一個字段,調用每一個字段的驗證方法

  接下來就看到LoginForm的驗證方法

    def validate(self):
        extra = {}
        for name in self._fields:
            #獲取每一個字段的鉤子函數 validate_name validate_pwd
            inline = getattr(self.__class__, 'validate_%s' % name, None)
            if inline is not None:
                extra[name] = [inline]
        '''
        前提是你有定義這麼一個函數
        添加完後extra = {
            name : [validate_name],
            pwd : [validate_pwd]
        }
        '''
        return super(Form, self).validate(extra)

   而在執行父類裏的驗證方法裏,就負責調用了每一個字段裏的驗證方法

    def validate(self, extra_validators=None):
        self._errors = None
        success = True
        '''
        extra_validators = {
            name : [validate_name],
            pwd : [validate_pwd]
        }
        '''
        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()
            #調用字段的驗證方法,並傳入鉤子函數,self爲form對象,到時候用它裏面的正則和要驗證的值
            if not field.validate(self, extra):
                success = False
        return success

   在字段的驗證方法中,有這麼一段,調用了_run_validation_chain方法

        if not stop_validation:
            '''
            validators=[
                validators.DataRequired(message='用戶名不能爲空.'),
                validators.Length(min=6, max=18, message='用戶名長度必須大於%(min)d且小於%(max)d')
            ]
            extra_validators=[鉤子函數]
            '''
            chain = itertools.chain(self.validators, extra_validators)
            stop_validation = self._run_validation_chain(form, chain)

   執行_run_validation_chain

        #循環每條驗證規則進行驗證
        for validator in validators:
            try:
                #validator要麼是鉤子函數 要麼是對象,對象則又去執行__call__方法
                validator(form, self)
            except StopValidation as e:
                if e.args and e.args[0]:
                    self.errors.append(e.args[0])
                return True
            except ValueError as e:
                self.errors.append(e.args[0])

        return False

 

 分析實例代碼

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')

app.debug = True


# 1.因爲 metaclass=FormMeta,因此LoginForm是由FormMeta建立
# 2. 執行 FormMeta.__init__
#       LoginForm._unbound_fields = None
#       LoginForm._wtforms_meta = None
# 3. 解釋字段:
#       name = simple.StringField(...)
#       pwd = simple.PasswordField(...)
#    結果:
#       LoginForm.name = UnboundField(simple.StringField,StringField的全部參數)
#       LoginForm.pwd = UnboundField(simple.PasswordField,PasswordField的全部參數)

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'}
    )
    def validate_name(self,form):
        pass

"""
print(LoginForm.__dict__)
LoginForm ={
	'__module__': '__main__', 
	'name': <1 UnboundField(StringField, (), {'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>, 
	'pwd': <2 UnboundField(PasswordField, (), {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
	'__doc__': None, 
	'_unbound_fields': None, 
	'_wtforms_meta': None
}
"""

@app.route('/login', methods=['GET', 'POST'])
def login():
    # 實例LoginForm
    # 1. 執行FormMeta的__call__方法
    """
        class Meta(DefaultMeta,):
            pass

        LoginForm ={
            '__module__': '__main__',
            'name': <2 UnboundField(StringField, (), {'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>,
            'pwd': <1 UnboundField(PasswordField, (), {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>,
            '__doc__': None,
            '_unbound_fields': [
                (name, UnboundField對象(1,simple.StringField,參數)),
                (pwd, UnboundField對象(2,simple.PasswordField,參數)),
            ],
            '_wtforms_meta': Meta

    }
    """
    # 2. 執行LoginForm的__new__方法
    #    pass
    # 3. 執行LoginForm的__init__方法
    """
     LoginForm ={
            '__module__': '__main__',
            'name': <2 UnboundField(StringField, (), {'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}})>,
            'pwd': <1 UnboundField(PasswordField, (), {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>,
            '__doc__': None,
            '_unbound_fields': [
                (name, UnboundField對象(1,simple.StringField,參數)),
                (pwd, UnboundField對象(2,simple.PasswordField,參數)),
            ],
            '_wtforms_meta': Meta

        }
    form = {
        _fields: {
                name: StringField對象(),
                pwd: PasswordField對象(),
            }
        name:  StringField對象(widget=widgets.TextInput()),
        pwd:  PasswordField對象(widget=widgets.PasswordInput())
    
        }
    
    """
    if request.method == 'GET':

        form = LoginForm()
        # form._fields['name']
        # form.name = StringField對象()
        """
        1. StringField對象.__str__
        2. StringField對象.__call__
        3. meta.render_field(StringField對象,)
        4. StringField對象.widget(field, **render_kw)
        5. 插件.__call__()
        """
        print(form.name) #
        """
        0. Form.__iter__: 返回全部字段對象
            1. StringField對象.__str__
            2. StringField對象.__call__
            3. meta.render_field(StringField對象,)
            4. StringField對象.widget(field, **render_kw)
            5. 插件.__call__()
        """
        for item in form:
            # item是fields中的每個字段
            print(item)

        return render_template('login.html',form=form)
    else:
        # 上述流程+
        # 從請求中獲取每一個值,再複製到到每一個字段對象中
        """
         form = {
            _fields: {
                    name: StringField對象(data=你輸入的用戶名),
                    pwd: PasswordField對象(pwd=你輸入的密碼),
                }
            name:  StringField對象(widget=widgets.TextInput(data=你輸入的用戶名)),
            pwd:  PasswordField對象(widget=widgets.PasswordInput(pwd=你輸入的密碼))
        
            }
        """
        # 請求發過來的值
        form = LoginForm(formdata=request.form) # 值.getlist('name')

        # 實例:編輯
        # # 從數據庫對象
        # form = LoginForm(obj='值') # 值.name/值.pwd
        #
        # # 字典 {}
        # form = LoginForm(data=request.form) # 值['name']

        # 1. 循環全部的字段
        # 2. 獲取每一個字段的鉤子函數
        # 3. 爲每一個字段執行他的驗證流程 字段.validate(鉤子函數+內置驗證規則)
        if form.validate():
            print(form.data)
        else:
            print(form.errors)


if __name__ == '__main__':
    app.run()
相關文章
相關標籤/搜索