没有找到好的Python数据模型验证(Validate)库-决定自己尝试造下轮子

236 views
Skip to first unread message

李海珍

unread,
Jan 10, 2014, 3:19:40 AM1/10/14
to pyth...@googlegroups.com
数据在存储之前是需要验证的,
但是我Google良久,没有找到Python在这方面的优秀的库,或者最佳实践指导之类的,
有一天,我发现了rails中的 的处理方式:
真是太喜欢了,写法真是简洁优雅啊.
为什么不用rails?因为项目基于Flask搞一年多了.
为什么不用django? 因为喜欢SQLAlchemy,Flask, jinja2
而且就算用django,它的验证也感觉不出特别的优雅.

还是回去我寻找基于SQLAlchemy的ORM的验证库吧, 没有什么理想的.
最近看到一个SAValidation, 功能弱不说,难以扩展,bug也不少,看下代码,也觉得有点乱.默认只能在session flush或者exec之前的验证然后抛出异常.

昨天我在想limodou前辈很早(大概在05年)就在使用SQLAlchemy,而且也写搞了不uliweb这样的框架.
然后我去github上找了找,
找到了这么一个类:
这真不是能让人很满意的结果,我当然不能说前辈的代码不好,
只是我想找汽车的时候,发现的却是轮子,这当然有点失望了.

回过头,我还是从之前发现的formencode库,学习,然后写成了现在的模式,
我不知道,自己造轮子是对还是错,发出来,请大家指点.
(1)验证方法, 我不太喜欢抛出异常,因为比较浪费资源
例如对于普通整形的验证:(及自定义的性别值验证)
我提供以下的方法:
def int_to_python(value,min=None,max=None,empty=False,default=0,error=None):
    """
    @type value: str | int
    :param value: 待验证的值
    :param min:  值的最小取值范围
    :param max:  最大取值范围
    :param empty: 是否可以为空
    :param default: 默认值
    :param error: 如果出错的话,可以指定的返回错误语句
    :return: 返回(value,error) 元组
    """
    error = error if error else "%s 非法的整数值".format(value)
    if value and isinstance(value,basestring):
        value = value.strip()
    if value in (None,''):
        return default,None if empty else None,error
    try:
        value = int(value)
        if min is not None and value < min:
            return None,error
        if max is not None and value > max:
            return None,error
        return value,None
    except (ValueError,TypeError):
        return None,error

def sex_to_python(value):
    """
    验证性别值,如果合法返回对应合法值
    @param value: 性别值
    @return:
    """
    return int_to_python(value,0,3,error="性别值不合法")


然后对于,数据模型列:
使用hybird属性
    #: 性别
    _sex = db.Column("sex", db.SmallInteger, nullable=False, default=SEX_SECRET, server_default=text('0'))

    @hybrid_property
    def sex(self):
        return self._sex

    @sex.setter
    def sex(self,sex):
         self._check_and_set(validator.sex_to_python,'_sex',sex)

    def _check_and_set(self,validate_func, real_attr, value):
        old_value = getattr(self,real_attr)
        if old_value == value:
            return
        val,err = validate_func(value)
        if err:
            self.add_error(real_attr,err)
        elif old_value != val:
            setattr(self,real_attr,val)

然后有一个Errors类的Model实例变量,用来存储对应属性的错误值:
    def add_error(self,attr,message,code=None):
        if not hasattr(self,'_errors'):
            self._errors = Errors()
        self._errors.add(attr,message,code)

    def add(self,attr,message,code=None):
        """
        @param attr: 字段属性
        @param message: 错误消息
        @param code: 可能指定的错误代码
        """
        self.messages[attr] = Msg(message,code)

请大家指点,哪里不对有什么需要改进的地方.谢谢.

刚加进CPyUG这个大家庭,之前定定阅列表很久没有出过声啊.




twinsant

unread,
Jan 10, 2014, 3:28:08 AM1/10/14
to Python.cn@google


2014/1/10 李海珍 <banx...@gmail.com>

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到 python-cn+...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/groups/opt_out。

whycrying

unread,
Jan 10, 2014, 3:54:32 AM1/10/14
to pyth...@googlegroups.com
我用了 WTForms 处理表单,于是表单校验也用了它内置的那些,内置不够用的话自己按照接口实现。
(不过 WTForms 默认对国际化支持不是很好,所以自个 hack 了一下……)


2014/1/10 李海珍 <banx...@gmail.com>

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到 python-cn+...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/groups/opt_out。

limodou

unread,
Jan 10, 2014, 6:06:08 AM1/10/14
to Python.cn@google
在uliweb中真正用来校验的是uliform。form包下的文件。validators只不过是在定义form中可以提供的校验函数。


2014/1/10 李海珍 <banx...@gmail.com>

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到 python-cn+...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/groups/opt_out。



--
I like python!
UliPad <<The Python Editor>>: http://code.google.com/p/ulipad/
UliWeb <<simple web framework>>: https://github.com/limodou/uliweb
My Blog: http://my.oschina.net/limodou

limodou

unread,
Jan 10, 2014, 6:09:22 AM1/10/14
to Python.cn@google
不过在uliweb中,ORM的确也可以校验,不过即很少这样做,基本上除了required以外,因为到了orm层的校验已经很难再向用户返回信息了。而form层的校验,则很方便返回出错结果。所以现在都是在form中校验,成功后直接保存ORM(除required以外,这个是内置的,其它的ORM层校验缺省没加,用户可以自已添加)。


2014/1/10 limodou <lim...@gmail.com>

Ping Yang

unread,
Jan 14, 2014, 9:40:05 AM1/14/14
to pyth...@googlegroups.com

用WTforms不好?我也用flask,不管前台js怎么验证,后台拿wtforms挡一下就成了。在orm层做验证,没什么必要吧?

teemo

unread,
Jan 15, 2014, 2:26:42 AM1/15/14
to python-cn
用WTforms验证,前台是不是必须用它的写法?这样前端很不灵活,复杂表单写起来头痛。
--
努力寻找不疲惫的方式

Ping Yang

unread,
Jan 15, 2014, 2:49:20 AM1/15/14
to pyth...@googlegroups.com
WTForms前端各表单项的渲染类也是可以定制的。见http://wtforms.readthedocs.org/en/latest/widgets.html

My Blog: Mr-Ping.com

李海珍

unread,
Jan 15, 2014, 4:11:32 AM1/15/14
to pyth...@googlegroups.com
如果用WTForms的话,会不会有点重复自己的感觉呢?
不过,看了下,WTForms 有2.x了看下感觉不是很不错了.
我主要是为移动端提供API

但是我还是根据自己的需要,写了自己的验证逻辑:
分享出来给大家看看:

首先有一个基于Descriptor的验证器:
class Validator(object):
    "注意多个此验证类的实例作用的类的多个实例,共享同一个验证方法,及参数,及配置"
    def __init__(self,attr,  validate_func,**kwargs):
        self.attr = attr
        self.validate_func = validate_func
        self.error_code = kwargs.pop('error_code',400)
        self.kwargs = kwargs

    def __get__(self, instance, owner):
        if instance is None:
            return getattr(owner,self.attr)
        return getattr(instance,self.attr)

    def __set__(self, instance, value):
        real_attr = self.attr
        old_value = getattr(instance, real_attr)
        if old_value == value:
            return
        if self.kwargs:
            val,err = self.validate_func(value)
        else:
            val, err = self.validate_func(value,**self.kwargs)
        if err:
            log.debug("INVALID {}:{}".format(self.attr,value))
            raise ValidateError(err, self.error_code, self.attr)
        elif old_value != val:
            setattr(instance, real_attr, val)

然后模型中:
    _name = db.Column('name', db.String(255), nullable=False)
    name = Validator('_name',validator.str_to_python,min_length=2,max_length=255,empty=False)


这样,模型声明及验证在在一起,看起来觉得比较好.
如果用WTForms的话,可能要比较远了,

不知道上面的写法有没有什么bug? 请大家指出

在 2014年1月15日星期三UTC+8下午3时49分20秒,PythonMap写道:

Zhou

unread,
Jan 21, 2014, 10:06:15 AM1/21/14
to pyth...@googlegroups.com

借地问一下,我post是一个json,Content-Type是application/json原本是想用json schema验证的,后来发现基本不可能前,因为所有request和response的结构都是动态的,还有属性依赖其他属性的存在与否或其他属性的值的情况。

现在我全部都是if结构做的验搞的挺痛苦得,难到不难,就是代码写的超麻烦,不清晰,有人有什么好办法么?

limodou

unread,
Jan 22, 2014, 5:47:35 AM1/22/14
to Python.cn@google
可以参考一下wtform或uliweb.form的一些实现。在uliweb中,一个Form就可以用于验证,它支持字段的验证和表单的验证,区别主要就是要处理的数据不同,字段验证只接收当前字段的值,所以无法校验共它值。表单验证是可以同时检查其它的值。所以最简单的做法就是都在表单验证中处理,不过这样就都要写程序来处理。如果只涉及字段,那么就可以定义Validator,而Validator是可以复用的,如果不喜欢validator,还可以针对字段写方法来验证。好象wtform差不多。


2014/1/21 Zhou <zhouqi...@gmail.com>



--

Goldfish Huang

unread,
Jan 22, 2014, 8:09:39 PM1/22/14
to pyth...@googlegroups.com
单就验证机制来说其实没啥。要具体地验证,需要写一个库来判断一个字符串是不是身份证,是不是电话号码,是不是数字。这些东西做起来比较麻烦。


--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到 python-cn+...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/groups/opt_out。



--
免服务器办公室聊天软件、笔记本、日记本:http://besteam.im/
Python及Qt相关Blog:http://hgoldfish.com/

Catstyle Lee

unread,
Jan 22, 2014, 8:42:59 PM1/22/14
to pyth...@googlegroups.com


2014/1/23 Goldfish Huang <hgol...@gmail.com>



--
BR,
/Catstyle_Lee

Jeffery

unread,
Jan 23, 2014, 12:32:41 AM1/23/14
to pyth...@googlegroups.com
刚刚看了sqlalchemy的文档,自己是带验证功能的

BR
--
BR,
/Catstyle_Lee

--
--
邮件来自: `CPyUG`华蟒用户组(中文Python技术邮件列表)
规则: http://code.google.com/p/cpyug/wiki/PythonCn
发言: pyth...@googlegroups.com
详情: http://code.google.com/p/cpyug/wiki/CpyUg
G+: https://plus.google.com/u/0/communities/108786798869709602787
严正: 理解列表! 智慧提问! http://wiki.woodpecker.org.cn/moin/AskForHelp
---
您收到此邮件是因为您订阅了 Google 网上论坛的“python-cn(华蟒用户组,CPyUG 邮件列表)”论坛。
要退订此论坛并停止接收此论坛的电子邮件,请发送电子邮件到 python-cn+...@googlegroups.com
要查看更多选项,请访问 https://groups.google.com/groups/opt_out。


--
Jeffery
 ___
/\__\ "What is the world coming to?"
\/__/  



limodou

unread,
Jan 23, 2014, 8:47:30 AM1/23/14
to Python.cn@google
很多情况下,用户的交互验证和Model关系不大。


2014/1/23 Jeffery <jeff...@gmail.com>



--
Reply all
Reply to author
Forward
0 new messages