Hi folks --

I'd like to convert all the view decorators built into Django to be
"universal" -- so they'll work to decorate *any* view, whether a
function, method, or class. I believe I've figured out a technique for
this, but I'd like some feedback on the approach before I dive in too
deep.

Right now view decorators (e.g. @login_required) only work on
functions. If you want to use a decorator on a method then you need to
"convert" the decorator using method_decorator(original_decorator).
You can't use view decorators on class-based views at all. This means
making a class-based view require login requires this awesomeness::

    class MyView(View):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(MyView, self.dispatch(*args, **kwargs)

This makes me sad. It's really counter-intuitive and relies on a
recognizing that functions and methods are different to even know to
look for method_decorator.

#14512 proposes a adding another view-decorator-factory for decorating
class-based views, which would turn the above into::

    @class_view_decorator(login_required)
    class MyView(View):
        ...

This makes me less sad, but still sad. Factory functions. Ugh.

I want @login_required to work for anything::

    @login_required
    class MyView(View):
        ...

    class Something(object):
        @login_required
        def my_view(self, request):
            ...

    @login_required
    def my_view(request):
        ...


Now, back in the day we somewhat had this: decorators tried to work
with both functions and methods. The technique turned out not to work
(see #12804) and was removed in [12399]. See
http://groups.google.com/group/django-developers/browse_thread/thread/3024b14a47f5404d
for the discussion.

I believe, however, I've figured out a different technique to make
this work: don't try to detect bound versus unbound methods, but
instead look for the HttpRequest object. It'll either be args[0] if
the view's a function, or args[1] if the view's a method. This
technique won't work for any old decorator, but it *will* work (I
think) for any decorator *designed to be applied only to views*.

I've written a proof-of-concept patch to @login_required (well,
@user_passes_test, actually):

    https://gist.github.com/1220375

The test suite passes with this, with one exception:
https://code.djangoproject.com/browser/django/trunk/tests/regressiontests/decorators/tests.py#L87.
I maintain that this test is broken and should be using RequestFactory
instead.

Can I get some thoughts on this technique and some feedback on whether
it's OK to apply to every decorator built into Django?

Jacob

-- 
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.

Reply via email to