A decorator decorator for Django views

Hitting a new level of meta, I just wrote a decorator decorator for Django views.

The context here is that I needed to write some custom view decorators that gave me access to the request object, since the normal built-in decorators like user_passes_test only offer access to the request.user object. While doing that, I wanted them to be able to apply to both function- and class-based views without having to use something like the method_decorator decorator, but still having it know to wrap dispatch on a class-based view unless otherwise specified.

Enter the decorator decorator:

from functools import wraps
from types import FunctionType

def class_or_function_view_decorator(class_method_to_wrap='dispatch'):
    '''
    A decorator that enables other decorators to decorate both function-
    and class-based views.  Can also be used on class-based views' methods.

    Usage:
        @class_or_function_view_decorator()
        def sayhello(f):
           """
           A decorator that says hello before the function is called.
           When used on a class, it will wrap the class's `dispatch` method.

           Usage:
                @sayhello
                def saybye():
                    print 'bye!'

                @sayhello
                def MyClass(object):
                    def dispatch(self):
                        print 'bye!'
           """
           @functools.wraps(f)
           def wrapped(*args, **kwargs):
                print 'hello!'
                return f(*args, **kwargs)
            return wrapped

        @class_or_function_view_decorator('__init__')
        def saybye(f):
            """
            When this decorator is used on a class, it will wrap the class's `__init__` method,
            unlike the previous example which wrapped `dispatch`.
            """
            @functools.wraps(f)
            def wrapped(*args, **kwargs):
                x = f(*args, **kwargs)
                print 'bye!'
                return x
            return wrapped

    This decorator is used to decorate a view decorator to enable it to be used on
    function-based views or on class-based views.  If the resulting decorator is used on class-based views, you can
    choose which method of the class it will decorate, defaulting to 'dispatch' if not provided.
    If used on function-based views, the resulting decorator works as normal.

    '''
    def outside_wrapper(f):
        @wraps(f)
        def wrapped(to_be_wrapped):
            if type(to_be_wrapped) is FunctionType:
                return f(to_be_wrapped)
            else:
                setattr(to_be_wrapped, class_method_to_wrap, f(getattr(to_be_wrapped, class_method_to_wrap)))
                return to_be_wrapped
        return wrapped
    return outside_wrapper

In short, this allows me to create a single decorator that can be used anywhere, like so:

@class_or_function_view_decorator()
def request_passes_test(test_func, login_url=None):
    """
    Decorator for views that checks that the session passes the given test,
    redirecting to the log-in page if necessary. The test should be a callable
    that takes the request object and returns True if the test passes.
    """

    def decorator(view_func):
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            if test_func(request):
                return view_func(request, *args, **kwargs)
            else:
                if login_url:
                    return redirect(login_url)
                else:
                    raise PermissionDenied()
        return _wrapped_view
    return decorator