Formencode OneOf 验证器与动态列表进行测试
Formencode OneOf validator with dynamic list to test against
我正在使用 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"只执行一次(我猜是在架构初始化或类似的过程中)。
每次我想检查我的用户输入 "code" 时,我都需要它 运行,我该怎么做?
感谢帮助
我不知道有什么方法可以使 formencode 执行您要求的操作。然而,由于这是 Python,我们可以做的事情几乎没有限制。
您可以通过将对 getActiveCodes
的调用包装在一个专门构建的 class 中来解决这个问题。包装器 class、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 可以像
一样使用
class ItemsEditSchema(Schema):
code = validators.OneOf(RefreshBeforeContainsCheck(getActiveCodes))
allow_extra_fields = True
在验证器架构中。
按照上面的实现方式,每次 OneOf
验证器执行 item in list
测试时都会调用 getActiveCodes
函数(其中我们的 class 作为list
),因为这会导致 RefreshBeforeContainsCheck.__contains__
被调用。现在,如果验证失败,OneOf
验证器会生成一条错误消息,列出 list
的所有元素;该案例由我们的 __iter__
实施处理。为了避免在验证错误的情况下调用数据库两次,我选择将 "database" 结果列表缓存为 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)})
最后我写了一个 Fancy Validator 而不是使用 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
我正在使用 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"只执行一次(我猜是在架构初始化或类似的过程中)。
每次我想检查我的用户输入 "code" 时,我都需要它 运行,我该怎么做?
感谢帮助
我不知道有什么方法可以使 formencode 执行您要求的操作。然而,由于这是 Python,我们可以做的事情几乎没有限制。
您可以通过将对 getActiveCodes
的调用包装在一个专门构建的 class 中来解决这个问题。包装器 class、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 可以像
class ItemsEditSchema(Schema):
code = validators.OneOf(RefreshBeforeContainsCheck(getActiveCodes))
allow_extra_fields = True
在验证器架构中。
按照上面的实现方式,每次 OneOf
验证器执行 item in list
测试时都会调用 getActiveCodes
函数(其中我们的 class 作为list
),因为这会导致 RefreshBeforeContainsCheck.__contains__
被调用。现在,如果验证失败,OneOf
验证器会生成一条错误消息,列出 list
的所有元素;该案例由我们的 __iter__
实施处理。为了避免在验证错误的情况下调用数据库两次,我选择将 "database" 结果列表缓存为 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)})
最后我写了一个 Fancy Validator 而不是使用 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