Hello

As a happy user of the class-based views, I wrote a couple of higher-level 
decorators to make it easy to apply all the standard decorators to them.

The first is an updated version of method_decorator, made to accept additional 
arguments and pass them to the original decorator:

class MyView(View):
    @method_decorator(never_cache)
    @method_decorator(login_required, login_url='/my-login')
    @method_decorator(permission_required, 'thing.change_thing')
    def dispatch(self, request, *args, **kwargs):
        ...

The second is class_view_decorator, to apply classic view decorators to the 
newer class-based views:

@class_view_decorator(never_cache)
@class_view_decorator(login_required, login_url='/my-login')
@class_view_decorator(permission_required, 'thing.change_thing')
class MyView(View):
    ...

In the application I'm developing they seem to be working ok--and saved me 
quite a bit of code clutter!

If you approve of this addition, I will develop proper unit tests and 
documentation as required.

-Tobia

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

from functools import update_wrapper

def method_decorator(decorator, *dec_args, **dec_kwargs):
    """
    Converts a function decorator into a method decorator, with or without
    decorator arguments.

    Does not support decorators with call but without arguments: @my_dec()

    Example:

    @method_decorator(never_cache)
    @method_decorator(login_required, login_url='/my-login')
    @method_decorator(permission_required, 'thing.change_thing')
    def dispatch(self, request, *args, **kwargs):
        ...
    """
    # 'func' is a function at the time it is passed to _dec, but will eventually
    # be a method of the class it is defined it.
    def _dec(func):
        def _wrapper(self, *args, **kwargs):
            if dec_args or dec_kwargs:
                @decorator(*dec_args, **dec_kwargs)
                def bound_func(*args2, **kwargs2):
                    return func(self, *args2, **kwargs2)
            else:
                @decorator
                def bound_func(*args2, **kwargs2):
                    return func(self, *args2, **kwargs2)
            # bound_func has the signature that 'decorator' expects i.e.  no
            # 'self' argument, but it is a closure over self so it can call
            # 'func' correctly.
            return bound_func(*args, **kwargs)
        # In case 'decorator' adds attributes to the function it decorates, we
        # want to copy those. We don't have access to bound_func in this scope,
        # but we can cheat by using it on a dummy function.
        if dec_args or dec_kwargs:
            @decorator(*dec_args, **dec_kwargs)
            def dummy(*args, **kwargs):
                pass
        else:
            @decorator
            def dummy(*args, **kwargs):
                pass
        update_wrapper(_wrapper, dummy)
        # Need to preserve any existing attributes of 'func', including the name.
        update_wrapper(_wrapper, func)
        return _wrapper
    update_wrapper(_dec, decorator)
    # Change the name to aid debugging.
    _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
    return _dec

def class_view_decorator(view_decorator, *args, **kwargs):
    """
    Converts a function decorator for regular Django views, with or without
    decorator arguments, into a class decorator for class-based views.

    Example:

    @class_view_decorator(never_cache)
    @class_view_decorator(login_required, login_url='/my-login')
    @class_view_decorator(permission_required, 'thing.change_thing')
    class MyView(View):
        ...
    """
    def apply_decorator(cls):
        # extend the original class and decorate its dispatch method
        class Decorated(cls):
            @method_decorator(view_decorator, *args, **kwargs)
            def dispatch(self, *args, **kwargs):
                return super(Decorated, self).dispatch(*args, **kwargs)
        # preserve name and module of the class, for debugging purposes
        Decorated.__module__, Decorated.__name__ = cls.__module__, cls.__name__
        return Decorated
    return apply_decorator

Reply via email to