这段时间在做用到tornado的项目,一般那些表单验证都直接交给sqlalchemy或者直接数据库提交捕捉异常,后来想到这种方式对数据的负担较大,而且也不安全,然后对必要的字段都加上了手动的验证
明确一下目标
定义LoginForm()格式
下面是Field
发现在重载魔法方法过程中,调用object的魔法方法是个不错的选择
if name:
return {'state':'error','data':'名称不可为空'}
类似这种验证
写多了后来发不仅不美观规范,而且后期修改和添加验证也很麻烦 ,就产生了写个类似Django里面的form那种表单验证,不过比那个简陋多啦。
我的习惯是用捕获异常的方式处理这种验证
先明确代码的层次关系
- UserForm
- CharField
- MinLengthValidate
- MaxLengthValidate
- IntegerField
- MaxValueValidate
- MinValueValidate
- CharField
类似这种层次关系
明确一下目标
- 编写多个验证Validation,用于在Field中复用,比如RequiredValidation(必填验证),minLengthValidation(最小长度验证),拥有自己的validate方法,不满足会抛异常
- 编写多个Field,可以加入Validation复用,也有自己的验证,如IntegerField(整数域),CheckListFIeld(选择字段),也拥有自己的validate
- 调用Field的validate时会调用他下面所有的Validation的validate方法
- 一个Form可以定义许多Field,需要验证的信息以及异常的消息在定义Form时给定,方便service里面调用
异常类
class FormException(Exception):
pass
定义LoginForm()格式
class LoginForm(BaseForm):
user = CharField(name='用户名', required=True)
password = CharField(name='密码', required=True)
t = CheckField(name='用户类型', required=True, checkList={1, 2, 3})
checkCode = CharField(name='验证码', required=True)
其中四个字段都为必填,t的值只能在1,2,3中选择
先放测试代码,
</pre></div><pre name="code" class="python">import unittest
from forms import LoginForm
from myExceptions import FormException
class LoginFormTestCase(unittest.TestCase):
def testLogin1(self):
try:
form = LoginForm()
form.user = 'admin'
form.password = 'sdfghjkbvcvbn'
form.checkCode = '12av'
form.t = 1
form.validation()
except FormException, e:
assert False
def testLogin2(self):
try:
form = LoginForm()
form.user = 'admin'
form.password = 'sdfghjkbvcvbn'
form.checkCode = '12av'
form.t = 4
form.validation()
assert False
except FormException, e:
pass
def testLogin3(self):
try:
form = LoginForm()
form.user = 'admin'
form.checkCode = '12av'
form.t = 1
form.validation()
assert False
except FormException, e:
pass
if __name__ == '__main__':
unittest.main()
下面放Validation代码
先是基本的父类
class Validation(object):
def __init__(self):
self.name = ''
self.value = ''
def validation(self):
pass
各种子Validation
class RequiredValidation(Validation):
'''
必填字段验证
'''
def validation(self):
if self.value is None or (
(type(self.value) == str or type(self.value) == unicode) and self.value.strip() == '' ):
raise FormException('%s不可为空' % self.name)
class MaxLengthValidation(Validation):
'''
最大长度验证
'''
def __init__(self, maxLength):
self.maxLength = maxLength
def validation(self):
if self.value and len(self.value) > self.maxLength:
raise FormException('%s不可大于%s位' % (self.name, self.maxLength))
class MinLengthValidation(Validation):
'''
最小长度验证
'''
def __init__(self, minLength):
self.minLength = minLength
def validation(self):
if self.value and len(self.value) < self.minLength:
raise FormException('%s不可小于%s位' % (self.name, self.minLength))
class MaxValueValidation(Validation):
'''
最大数值验证
'''
def __init__(self, maxValue):
self.maxValue = maxValue
def validation(self):
if self.value and self.value > self.maxValue:
raise FormException('%s不可大于%s' % (self.name, self.maxValue))
class MinValueValidation(Validation):
'''
最小数值验证
'''
def __init__(self, minValue):
self.minValue = minValue
def validation(self):
if self.value and self.value < self.minValue:
raise FormException('%s不可小于%s' % (self.name, self.minValue))
下面是Field
先是父Field
class Field(object):
name = ''
value = ''
def __init__(self, name):
self.name = name
self.validations = []
def __setattr__(self, key, value):
object.__setattr__(self, key, value)
if key == 'value':
for val in self.validations:
val.__setattr__(key, value)
def add_validations(self, validation):
validation.name = self.name
self.validations.append(validation)
def validation(self):
for val in self.validations:
val.validation()
发现在重载魔法方法过程中,调用object的魔法方法是个不错的选择
下面是子Field
lass IntegerField(Field):
def __init__(self, name='', required=False, maxValue=None, minValue=None):
Field.__init__(self, name)
if required:
self.add_validations(RequiredValidation())
if not maxValue is None:
self.add_validations(MaxLengthValidation(maxValue))
if not minValue is None:
self.add_validations(MinLengthValidation(minValue))
def validation(self):
Field.validation(self)
if self.value and (not util.is_int(str(self.value))):
raise FormException('%s不是整数' % self.name)
class FloatField(Field):
def __init__(self, name='', required=False, maxValue=None, minValue=None):
Field.__init__(self, name)
if required:
self.add_validations(RequiredValidation())
if not maxValue is None:
self.add_validations(MaxLengthValidation(maxValue))
if not minValue is None:
self.add_validations(MinLengthValidation(minValue))
def validation(self):
Field.validation(self)
if self.value and (not util.is_float(str(self.value))):
raise FormException('%s不是小数' % self.name)
class EmailField(Field):
def __init__(self, name='', required=False):
Field.__init__(self, name)
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
regex = re.compile("^[a-zA-Z0-9_\.]+@[a-zA-Z0-9-]+[\.a-zA-Z]+$")
if self.value and (not regex.match(self.value)):
raise FormException('%s不是邮箱格式' % self.name)
class UrlField(Field):
def __init__(self, name='', required=False):
Field.__init__(self, name)
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
regex = re.compile(
"^(http|https|ftp)\\://([a-zA-Z0-9\\.\\-]+(\\:[a-zA-Z0-9\\.&%\\$\\-]+)*@)?((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|([a-zA-Z0-9\\-]+\\.)*[a-zA-Z0-9\\-]+\\.[a-zA-Z]{2,4})(\\:[0-9]+)?(/[^/][a-zA-Z0-9\\.\\,\\?\\'\\\\/\\+&%\\$\\=~_\\-@]*)*$")
if self.value and not (regex.match(self.value)):
raise FormException('%s不是url格式' % self.name)
class CheckField(Field):
def __init__(self, name='', required=False, checkList={}):
Field.__init__(self, name)
self.checkList = checkList
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
if self.value and (not self.value in self.checkList):
raise FormException('%s不是合法值' % self.name)
class BirthdayField(Field):
def __init__(self, name='', required=False):
Field.__init__(self, name)
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
regex = re.compile("^\d{4}[\-|\s+|/]\d{1,2}[\-|\s+|/]\d{1,2}$")
if self.value and (not regex.match(self.value)):
raise FormException('%s不是生日格式' % self.name)
class RegexField(Field):
def __init__(self, name='', required=False, regex=''):
Field.__init__(self, name)
self.regex = regex
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
regex = re.compile(self.regex)
if self.value and (not regex.match(self.value)):
raise FormException('%s格式错误' % self.name)
class JsonField(Field):
def __init__(self, name='', required=False, regex=''):
Field.__init__(self, name)
self.regex = regex
if required:
self.add_validations(RequiredValidation())
def validation(self):
Field.validation(self)
if self.value:
try:
ujson.loads(self.value)
except Exception, e:
raise FormException('%s格式错误' % self.name)
写了一些比较常用的Field验证
最后还有个最大的Form
父Form
class BaseForm(object):
def __init__(self):
for key in [x for x in dir(self) if isinstance(object.__getattribute__(self, x), Field)]:
object.__setattr__(self, key, copy.deepcopy(self.__getattribute__(key)))
def setWithDict(self, d):
fieldKeys = [x for x in dir(self) if isinstance(object.__getattribute__(self, x), Field)]
for key, value in d.items():
if key in fieldKeys:
self.__setattr__(key, value)
def __setattr__(self, key, value):
field = object.__getattribute__(self, key)
field.value = value
def validation(self):
for key in [x for x in dir(self) if not x.startswith('_')]:
if isinstance(object.__getattribute__(self, key), Field):
field = object.__getattribute__(self, key)
field.validation()
也就是调用Form的validation会调用他所有属性Field的validation方法,
在这值得一提的是
我一开始并没有重载__init__,后来发现创建的Form里面的属性竟是同一个对象,验证时会互相覆盖修改!
这是我不希望看到的,主要是因为类的定义属性那边引用赋值了,最后在__init__方法里面复制了对象
这样写完了后既可以进行验证,会抛出实现规定好的异常信息
OVER