Formencode OneOf验证器与动态列表进行测试

时间:2015-05-05 11:01:50

标签: python validation turbogears2 formencode

我正在使用formencode 1.3.0a1(以及turbogeras 2.3.4)并遇到验证器OneOf的问题。

我想根据数据库中的列表验证一些输入。 这是我的验证模式和获取列表的方法:

from formencode import Schema, validators

def getActiveCodes():
    codes = DBSession.query(SomeObject.code).all()
    codes = [str(x[0]) for x in codes]
    return codes

class itemsEditSchema(Schema):
    code = validators.OneOf(getActiveCodes())
    allow_extra_fields = True

方法“getActiveCodes”只执行一次(我想在架构初始化期间或类似的东西)。

每当我想检查用户输入的“代码”时,我都需要它运行,我该怎么做?

感谢您的帮助

2 个答案:

答案 0 :(得分:1)

我不知道如何让 formencode 做你所要求的。但是,由于这是Python,我们可以做的事情几乎没有限制。

您可以通过在专用类中包含对getActiveCodes的调用来解决此问题。包装类RefreshBeforeContainsCheck将实现特殊方法__iter____contains__,以提供用作可迭代对象的必要接口:

from formencode import Schema, validators, Invalid

class RefreshBeforeContainsCheck(object):
    def __init__(self, func):
        self._func = func
        self._current_list = None

    def __iter__(self):
        print '__iter__ was called.'
        #return iter(self._func())  # Could have refreshed here too, but ...
        return iter(self._current_list)

    def __contains__(self, item):
        print '__contains__ was called.'
        self._current_list = self._func()  # Refresh list.
        return item in self._current_list

我添加了print语句,以便更清楚地了解它在运行时的行为方式。然后可以像{/ p>一样使用RefreshBeforeContainsCheck

class ItemsEditSchema(Schema):
    code = validators.OneOf(RefreshBeforeContainsCheck(getActiveCodes))
    allow_extra_fields = True

在验证程序架构中。

上面实现的方式,每次getActiveCodes验证器执行OneOf测试(我们的类充当item in list)时,都会调用list函数,因为这会导致RefreshBeforeContainsCheck.__contains__被调用。现在,如果验证失败,OneOf验证器会生成一条错误消息,列出list的所有元素;该案例由我们的__iter__实施处理。为避免在验证错误的情况下调用数据库两次,我选择缓存"数据库"结果列表为self._current_list,但是否合适取决于您的需求。

我已经为此创建了一个要点:https://gist.github.com/mtr/9719d08f1bbace9ebdf6,基本上创建了一个使用以下代码使用上述代码的示例。

def getActiveCodes():
    # This function could have performed a database lookup.
    print 'getActivityCodes() was called.'
    codes = map(str, [1, 2, 3, 4])
    return codes

def validate_input(schema, value):
    print 'Validating: value: {!r}'.format(value)
    try:
        schema.to_python(value)
    except Invalid, error:
        print 'error: {!r}'.format(error)

    print

def main():
    schema = ItemsEditSchema()

    validate_input(schema, {'code': '3'})
    validate_input(schema, {'code': '4'})
    validate_input(schema, {'code': '5'})

要点的输出是:

Validating: value: {'code': '3'}
__contains__ was called.
getActivityCodes() was called.

Validating: value: {'code': '4'}
__contains__ was called.
getActivityCodes() was called.

Validating: value: {'code': '5'}
__contains__ was called.
getActivityCodes() was called.
__iter__ was called.
error: Invalid("code: Value must be one of: 1; 2; 3; 4 (not '5')",
   {'code': '5'}, None, None, 
   {'code': Invalid(u"Value must be one of: 1; 2; 3; 4 (not '5')",
       '5', None, None, None)})

答案 1 :(得分:0)

最后我用FancyValidtor而不是OneOf,这是我的代码:

class codeCheck(FancyValidator):
    def to_python(self, value, state=None):
        if value==None:
            raise Invalid('missing a value', value, state)
        return super(codeCheck,self).to_python(value,state)

    def _validate_python(self, value, state):
        codes = DBSession.query(Code).all()
        if value not in codes:
            raise Invalid('wrong code',value, state)
        return value


class itemsEditSchema(Schema):
    code = codeCheck ()
    allow_extra_fields = True