简体   繁体   中英

multiple python decorators

I have a Django Model (MyModel) with a decorated method (my_method). I expect the decorators to perform some checks on my_method:

  • if the checks succeed, my_method should return a string;

  • if the checks do not succeed, my_method should return the failure messages returned by the decorators.

The logic is the following:

# 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

So, if decorator1 returns a failure, I expect an_instance_of_my_model_instance.my_method(request) to return ('failure', 'message'). If decorator1 succeeded but not decorator2, I would expect ('another failure', 'message'). And if all the test were passed, u'The result that must be returned if all the checks performed by the decorator succeed'

I do not know how to check in decorator2 if decorator1's checks were successfully passed. I tried to do it by checking the type() of the method_to_decorate in decorator2, but it seems that type uses the original method itself, not its result as returned by decorator1 (as if decorators did not know about the operations performed by previous decorators).

Thank you in advance!

You'll need to swap the @decorator1 and @decorator2 lines if you want decorator2 to check up on whatever decorator1 returned:

@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'

Now decorator2 will wrap whatever method decorator1 returned, and you can thus inspect what that method returns.

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

The decorators will be called in the order you put them above the decorated method, and given your program structure, decorator2 doesn't get called if decorator1 fails, so there's no need to check if decorator1 was successful in decorator2 .

A slightly simpler example...

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)

...which prints...

failed in decorator 1
failed in decorator 2
success

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM