0

我正在使用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”只执行一次(我猜是在模式初始化或类似的过程中)。

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

谢谢您的帮助

4

2 回答 2

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

我添加了打印语句,以使其在运行时的行为更加清晰。然后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)})
于 2015-10-26T09:24:39.487 回答
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
于 2015-11-08T10:37:22.547 回答