2013-06-19 77 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