自定义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格式错误'}) 

 

posted @ 2016-05-17 17:18  巧妹儿  阅读(5693)  评论(0编辑  收藏  举报