python26:自定義form表單驗證


一、自定義Form的原理

1.1 各種form表單驗證比較

只有python提供了form表單驗證,其他的都沒有提供。django提供的功能還不夠強大。最強大的是微軟的ASP.NET!我們可以自己寫一個來實現。

 

1.2 【不推薦】通過request.post獲取用戶輸入內容

用戶輸入表單==>用戶輸入的檢測

1)我們之前是創建一個form

2)用戶提交表單,我們通過request.post拿到數據,然后封裝到Form(數據)里面

3obj.is_valid方法,來檢查用戶輸入的內容,跟Form()定義的,是否匹配。

 

問題:

request.post 獲取用戶輸入的內容,它知道用戶輸入了幾個嗎?

 

1.3 【推薦】通過自定義Form類,通過類對象來獲取

1.3.1如何獲取類的所有靜態字段:

所以,通過request.post取數據不好,所以我們可以用form類。

如:

Form類:

u = xxxx

p = xxxx

 

我們先創建一個Form對象:

obj = Form()

 

 

for i in Form類中的所有東西:

 

問題:

Form類:這些都是靜態字段,靜態字段屬於類。

如何獲取一個類的所有靜態字段?

 

'''

這些靜態屬性是屬於類的

這就是通過打印類的字典,來獲取類的靜態屬性

'''

class Foo(object):

    p=123

    u=456

 

print Foo.__dict__

如果要循環就是循環類的所有東西:

 

# for k,v in Foo類的所有東西:

 

'''

 

打印結果:

{'__module__': '__main__', 'p': 123, 'u': 456, '__dict__':

<attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}

'''

 

1.3.2 通過類對象來獲取所有實例屬性

'''

如果把屬性寫到初始化里,我們要找這些屬性的話,就要找類的對象的字典了(這些屬性屬於類的實例)

'''

class Foo2(object):

    def __init__(self):

        self.u='wang'

        self.p=123

如果要循環就是循環類的對象所有東西:

 

# for k,v in Foo2對象所有東西:

obj=Foo2()

print obj.__dict__

 

打印結果

# {'p': 123, 'u': wang}

 

# for k,v in 對象的所有東西:

1.3.3 自定義form驗證原理(類對象獲取對象屬性)

'''

我們先定義一個類,類里面有多少字段,是我們程序員控制的。

我們可以根據Foo類來生成頁面的標簽!

用戶提交數據的時候,我們先創建Foo類對象,然后再循環對象里所有的東西。

'''

 

#這里的obj為類的實例化對象

 

for k,v in obj.__dict__.iteritems():

    print k,v   # k代表屬性名:如'p', v代表值:如:123

    # request.POST[k] # 如果循環,來獲取request.POST[k],其實就是獲取用戶輸入的數據

    # 如果用戶輸入的是alexrequest.POST[k]就等於alex

    # 所以,我們通過循環自己的form來去前端取什么數據。是以我們在Form定義的項目為主,跟前端寫多少沒關系。

 

 

 

二、自定義Form實例1

 

2.1 獲取用戶輸入的值

 

就是創建一個web程序,只要請求一進來,訪問/index,其實就是訪問MainHandler類的get方法或者post方法

 

目錄結構如下:

 

 

運行:form_framework.py

瀏覽器訪問:http://localhost:8888/index

這時,就執行了MainHandler類的get方法,就是渲染index.html頁面

 

 

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        host = self.get_argument('host')    # 在Tornado里,獲得用戶的輸入,都是用get_argument
        print host

 

  

 

重新運行:form_framework.py

然后輸入:hostname內容,然后提交

 

 

此時python后台就打印:wang

 

2.2 自定義form類,打印對象的k,v

既然能獲取單個屬性,我們就可以循環form類對象來獲取對象的所有屬性(用戶輸入數據)

 

我們定義一個Form類,然后定義屬性名(注意,跟form表單的name名要對應)

然后循環

class Form(object):
    def __init__(self):
        self.host = None
        self.ip = None
        self.port = None
        self.phone = None

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        # host = self.get_argument('host')    # 在Tornado里,獲得用戶的輸入,都是用get_argument
        # print host
        obj=Form()
        for k,v in obj.__dict__.iteritems():
            print k,v

 

 

現在后台打印:

ip None

host None

port None

phone None

 

我們可以通過自定義的form來獲取用戶提交的數據,如果index.html,多了一個地址欄,

<p>address: <input type="text" name="address" /> </p>

但是form類里沒定義,我們也不管它。

也就是我們只收集form類定義的對象屬性

 

加上:self.get_argument(k)

def post(self, *args, **kwargs):
    # host = self.get_argument('host')    # 在Tornado里,獲得用戶的輸入,都是用get_argument
    # print host
    obj=Form()
    for k,v in obj.__dict__.iteritems():


        print k,v,self.get_argument(k)  # 對象的k和值,用戶輸入的值(通過跟form表單里的name名對應)

 

 

此時再從瀏覽器輸入表單內容,提交

 

 

python后台打印結果:

ip None 10.0.0.1

host None wang

port None 22

phone None 123456

 

沒有address,因為我們不管它。這樣就做到了,我們在Form類里寫了哪些字段,就取用戶輸入的哪些數據。

2.3 用正則來驗證用戶輸入數據

我們為啥要寫Form類呢?因為要做驗證!

 

k是字段,vform對象的值,self.get_argument(k)是用戶輸入的值。

我們把v改成正則表達式,我們用正則來驗證用戶輸入的值,如果驗證成功,就代表合法的,不成功就不合法。

1)我們先改寫Form類,把值改成正則表達式:

class Form(object):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = '(\d+)'
        self.phone = '^1[3|4|5|8][0-9]\d{8}$'

 

 

然后我們,通過正則驗證,如果循環過程,有一個驗證不成功,flag=False,打印錯誤:

def post(self, *args, **kwargs):
    # 在請求的時候,默認是驗證成功的
    flag= True
    # host = self.get_argument('host')    # 在Tornado里,獲得用戶的輸入,都是用get_argument
    # print host
    obj=Form()
    for k,v in obj.__dict__.iteritems():
        # k,對象的字典
        # v,對象中字段對應的值,正則
        # self.get_argument(k),用戶輸入的值(通過跟form表單里的name名對應)
        # 我們用正則來驗證用戶輸入的值,如果驗證成功,就代表合法的,不成功就不合法。
        # print k,v,self.get_argument(k)
        import re
        if re.match(v,self.get_argument(k)):    # 如果符合,則返回一個對象,如果不符合就返回一個None
            pass
        else:
            flag = False    # 在循環的過程中,一旦有一個不滿足,就是false了。

    if flag:
        self.write('ok')
    else:
        self.write('error')

  

 

重啟:form_framework.py

 

然后在表單輸入錯誤的格式,提交后就返回error

 

 

輸入格式錯誤,返回:error

 

 

2.4 把驗證寫到form類里

因為必須從Form類里獲取正則表達式,再做驗證,所以,我們直接把驗證寫到Form類里,把MainHandler類的post只做主函數就行了。

 

1)首先把:or k,v in obj.__dict__.iteritems(): 移動到Form

這里的obj,就是Form的實例化對象,在Form里就是指self

 

 

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        # 在請求的時候,默認是驗證成功的
        flag= True
        obj=Form()
        #for k,v in obj.__dict__.iteritems():    #移動到Form類里
            import re
            if re.match(v,self.get_argument(k)):    

                pass
            else:
                flag = False    # 在循環的過程中,一旦有一個不滿足,就是false了。

        if flag:
            self.write('ok')
        else:
            self.write('error')

 

 

class Form(object):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = '(\d+)'
        self.phone = '^1[3|4|5|8][0-9]\d{8}$'
    def is_valid(self):
        for k,v in self.__dict__.iteritems():    # 移動到這里,obj改成self
            import re

 

 

2)傳代碼:if re.match(v,self.get_argument(k)):  

 

看上面代碼

if re.match(v,self.get_argument(k)):  

post函數里的self,指的是MainHandler這個類的對象,

我們把is_valid函數傳個參數,request

is_valid函數里改寫代碼為:if re.match(v,request.get_argument(k)): 

其實這里的request就是:MainHandler對象,

is_valid函數在MainHandler里被調用,把自己的對象self傳進去。

 

 

 

3)把flag=True,分別定義在兩個函數里

改寫后的代碼如下:

class Form(object):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = '(\d+)'
        self.phone = '^1[3|4|5|8][0-9]\d{8}$'
    def is_valid(self):
        # 在請求的時候,默認是驗證成功的
        flag= True
        for k,v in self.__dict__.iteritems():
            import re
            import re
            if re.match(v,self.get_argument(k)):    # 如果符合,則返回一個對象,如果不符合就返回一個None
                pass
            else:
                flag = False    # 在循環的過程中,一旦有一個不滿足,就是false了。

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index.html')

    def post(self, *args, **kwargs):
        # host = self.get_argument('host')    # 在Tornado里,獲得用戶的輸入,都是用get_argument
        # print host
        flag= True
        obj=Form()
        obj.is_valid(self)   # 把自己的類對象self傳到MainHandler里。
        if flag:
            self.write('ok')
        else:
            self.write('error')

 

三、Form類優化

3.1 is_valid放到基類里

django里,每一個表單就是一個form,例如LoginFormAssetForm。。。

在你創建很多form的時候,但是is_valid都一樣。你需要在每個Form類里都寫一遍嗎?

不需要,你可以用基類。

 

class BaseForm(object):
    def is_valid(self):
        # 在請求的時候,默認是驗證成功的
        flag= True
        for k,v in self.__dict__.iteritems():
            import re
            import re
            if re.match(v,self.get_argument(k)):    # 如果符合,則返回一個對象,如果不符合就返回一個None
                pass
            else:
                flag = False    # 在循環的過程中,一旦有一個不滿足,就是false了。

class Form(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
        self.port = '(\d+)'
        self.phone = '^1[3|4|5|8][0-9]\d{8}$'

 

class LoginForm(object):
    def __init__(self):
        self.name= "(.*)"

 

 

 

 

3.2 正則表達式分門別類

如果多個表單有同一個正則,例如ip地址,就會重復寫同樣正則了。

我們可以針對每個類型的驗證,單獨寫正則表達式類。

調用的時候,調用這個字段的正則類的對象就行了。這樣就提高重用性了。

 

class ValidateIpv4(object):
    ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')


class ValidatePhone(object):
    phone_re = re.compile(r'^1[3|4|5|8][0-9]\d{8}$')


class Form(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = ValidateIpv4.ipv4_re
        self.port = '(\d+)'
        self.phone = ValidatePhone.phone_re

  

  

 

 

3.3 驗證后返回成功或失敗信息

django里,驗證無論成功或者失敗,都返回信息

對於IP來說,如果IP錯誤的話,也會有IP格式錯誤的提示。

 

django中是這么做的:

from django import forms

import re
from django.core.exceptions import ValidationError

 

def validate_ipv4(value):          # ip地址 驗證例如:10.1.6.10
    ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
    if not ipv4_re.match(value):    # 這里做了驗證,如果不成功,則報錯
        raise ValidationError('IP段格式錯誤.')    # ValidationError是django的方法

 

class AddressPoolForm(forms.Form):
    getway = forms.CharField(validators=[validate_ipv4, ],          # 驗證ip地址
                             error_messages={'required': u'網關不能為空', 'invalid': u'網關格式錯誤'},
                             widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': '網關'}))

 

 

 

 

那在自定義的Form里怎么做呢?

 

那在自定義的Form里怎么做呢?

required的值是傳進來的:

requiredTrue  表示:字段必須要填,並且驗證

requiredFalse  表示:字段可填可不填,不做驗證

假如,requiredFalse,則不做驗證,直接返回值。

如果required=True:則判斷:如果沒有自定義'required''valid'錯誤信息,則用默認的(Field提供)

如果驗證成功,則返回正則匹配的內容。

import tornado.ioloop
import tornado.web
import re


class Field(object):
    def __init__(self, error_msg_dict, required):
        self.id_valid = False
        self.value = None
        self.error = None
        self.name = None
        self.error_msg = error_msg_dict
        self.required = required

    def match(self, name, value):
        self.name = name

        if not self.required:  # 假如,required是False,則不做驗證,直接返回值。
            self.id_valid = True
            self.value = value
        else:
            if not value:  # 假如用戶沒傳值,則給出require報錯信息:
                if self.error_msg.get('required', None):  # 先看有沒有自定義的'required'錯誤信息,有就用自定義的,沒有則用默認的。
                    self.error = self.error_msg['required']
                else:
                    self.error = "%s is required" % name
            else:  # 如果用戶傳來值,則做驗證,如果驗證成功,則返回匹配結果,沒成功,則返回自定義(優先級高)或默認信息
                ret = re.match(self.REGULAR, value)
                if ret:
                    self.id_valid = True  # 是正則驗證成功的標識
                    self.value = ret.group()
                else:  # 先看有沒有自定義的'valid'錯誤信息,有就用自定義的,沒有則用默認的。
                    if self.error_msg.get('valid', None):
                        self.error = self.error_msg['valid']
                    else:
                        self.error = "%s is invalid" % name


# 如果required=True:則判斷:如果有自定義了'required'和'valid'錯誤,則用自定義的
class IPField(Field):
    REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"

    def __init__(self, error_msg_dict=None, required=True):
        error_msg = {}  # {'required': 'IP不能為空', 'valid': 'IP格式錯誤'}
        if error_msg_dict:
            error_msg.update(error_msg_dict)
        # 繼承基類的__init__方法,同時,把參數(error_msg,required)傳給基類__init__
        super(IPField, self).__init__(error_msg_dict=error_msg, required=required)

 

說明:

class IPField(Field):里的:

# 外面傳參:error_msg_dict 可以定制化錯誤信息!這里required默認值是True

def __init__(self, error_msg_dict=None, required=True):

        error_msg = {}  # {'required': 'IP不能為空', 'valid': 'IP格式錯誤'} 

        if error_msg_dict:

            error_msg.update(error_msg_dict) # 把定制化的錯誤信息來更新error_msg這個字典(也就是定制化錯誤優先級最高!)

 

 

 

這時候調用IPField,就可以隨意傳自定義的錯誤信息了!required=True是默認值,可以不用傳

class Form(BaseForm):
    def __init__(self):
        # required=True是默認值,可以不用傳
        self.ip = IPField(error_msg_dict={'required': '我的IP不能為空', 'valid': '我的IP格式錯誤'}) 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM