2013-06-19 79 views
1

我有一個裝飾方法(my_method)的Django模型(MyModel)。 我期待裝飾對my_method進行一些檢查:多個python裝飾器

  • 如果檢查成功,my_method應該返回一個字符串;

  • 如果檢查不成功,my_method應返回裝飾器返回的失敗消息。

的邏輯如下:

# models.py 
class MyModel(models.Model): 
    @decorator1 
    @decorator2 
    def my_method(self, request, *args, **kwargs): 
     return u'The result that must be returned if all the checks performed by the decorator succeed' 


# decorators.py 
from functools import wraps 

# decorator1 checks if certain conditions are met. If yes, it returns the decorated method (method_to_decorate); if not, it returns a tuple 
def decorator1(method_to_decorate): 
    @wraps(method_to_decorate) 
    def wrapper1(self, request, *args, **kwargs): 
     if a_condition : 
      return method_to_decorate(self, request, *args, **kwargs) 
     else: 
      # we return a failure message 
      return ('failure', 'message') 
    return wrapper1 

# in decorator2, we want to know if the check performed by decorator1 was successful 
# in case of success, we perform decorator2's check 
# in case of failure, decorator2 should simply pass the failure message returned by decorator1 
def decorator2(method_to_decorate): 
    @wraps(method_to_decorate) 
    def wrapper2(self, request, *args, **kwargs): 

     # if the first decorator succeeded 
     if decorator1_s_test_was_successful: 
      # we check if the conditions of the second decorator are met 
      if decorator2_s_test_was_successful: 
       # we return the method 
       return method_to_decorate(self, request, *args, **kwargs) 
      else: 
       # we return a failure message 
       return ('another failure', 'message') 
    # if the first decorator did not succeed 
     else: # decorator1 returned a tuple : ('failure', 'message') 
      return the_failure_that_decorator1_returned 
    return wrapper2 

所以,如果decorator1返回一個失敗,我期望an_instance_of_my_model_instance.my_method(請求)返回( '故障', '消息')。如果decorator1成功但不是decorator2,我會期待('另一個失敗','消息')。如果所有的測試都通過了,那麼如果裝飾器執行的所有檢查都成功,就必須返回結果'

如果decorator1的檢查成功通過,我不知道如何檢查裝飾器2。我試圖通過檢查decorator2中method_to_decorate的類型()來完成它,但似乎該類型使用原始方法本身,而不是decorator1返回的結果(就像裝飾器不知道以前裝飾器所執行的操作一樣) 。

預先感謝您!

回答

3

,如果你想decorator2檢查了在你需要交換@decorator1@decorator2線無論decorator1返回:

@decorator2 
@decorator1 
def my_method(self, request, *args, **kwargs): 
    return u'The result that must be returned if all the checks performed by the decorator succeed' 

現在decorator2將包裹任何方法decorator1返回,這樣的話你可以檢查那個方法返回。

def decorator2(method_to_decorate): 
    @wraps(method_to_decorate) 
    def wrapper2(self, request, *args, **kwargs): 

     result = method_to_decorate(self, request, *args, **kwargs) 

     if isinstance(result, tuple) and result and result[0] == 'failure': 
      # decorator1 returned a failure 
      return result 
     else: 
      # decorator1 passed through the wrapped method call 
      if decorator2_s_test_was_successful: 
       return result 
      else: 
       return ('another failure', 'message') 

    return wrapper2 
+0

好的,謝謝你的Martijn,工程。我害怕顛倒裝飾器的順序,但它的確有訣竅。 – Raphael

1

的裝飾會叫你把它們裝飾方法上面的順序,並給予你的程序結構,decorator2不會被調用,如果decorator1失敗,所以沒有必要檢查是否decorator1是成功的decorator2

稍微簡單的例子...

from functools import wraps 


def decorator1(f): 
    @wraps(f) 
    def wrapper(a): 
     if a >= 1: 
      return f(a) 
     return 'failed in decorator 1' 
    return wrapper 

def decorator2(f): 
    @wraps(f) 
    def wrapper(a): 
     if a >= 2: 
      return f(a) 
     return 'failed in decorator 2' 
    return wrapper 

@decorator1 
@decorator2 
def my_func(a): 
    return 'success' 


print my_func(0) 
print my_func(1) 
print my_func(2) 

...它打印...

failed in decorator 1 
failed in decorator 2 
success 
+0

謝謝艾雅,這是一個非常明確的解釋! – Raphael