zoukankan      html  css  js  c++  java
  • 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格式错误'}) 
  • 相关阅读:
    5.4Java Collections工具类 != Collection接口没关系
    4.30Java 手动敲简易的HashSet
    5.4Java使用容器存储表格数据
    4.30Java Iterator迭代器遍历容器元素(List/Set/Map)
    5.4Java IO流开篇
    windows管理规范WMI
    META标签的奥妙
    C#2.0泛型--Dictionary,List用法
    Win32类及其管理对象
    Asp.net中GridView使用详解(引)
  • 原文地址:https://www.cnblogs.com/wangqiaomei/p/5502439.html
Copyright © 2011-2022 走看看