2015-05-05 53 views
0

我正在使用formencode 1.3.0a1(和turbogeras 2.3.4)並遇到驗證器OneOf的問題。Formencode 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」只是一次(我的模式init或類似的東西在猜)執行。

我需要它每次運行時,我想檢查我的用戶輸入「代碼」,我該怎麼做?

感謝您的幫助

+0

你有沒有看到我的回答如下?如果是這樣,是否有任何進一步的幫助或我可以提供的信息,以便您可以接受? –

回答

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 
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)}) 
+0

感謝您的詳細解答。我最終編寫了一個FacnyValidator,而不是使用一個驗證器 –