Decoration of class based views

192 views
Skip to first unread message

Fabrizio Messina

unread,
Aug 6, 2015, 7:21:36 AM8/6/15
to Django developers (Contributions to Django itself)
Hello I would like to ask why the class based views documentation seems so much ugly. Some developers probably are scared by these just because the decoration is ugly, the documentation offers three ways:

Decorate the Klass().dispach() method of the class, wrapping the decorators in another decorator:

@method_decorator(decorator_1)
def dispatch(request, *args, **kwargs):
 
pass

Wrapping the Klass.as_view() resulting in an arguably ugly:

decorator_1( decorator_2( .. ( .. decorator_n( Klass.as_view() ) .. ) .. ))

In my opinion this is an issue because decoration is actually cool and the alternative is to use the much more dreadful Multiple Inheritance, that is the documented third way.


I personally use reduce syntax:

view = reduce(lambda x, f: f(x), (decorator_1, decorator_2, .., decorator_n), Klass.as_view())

Probably there are better ways but still to me it seems to me that something like this is more readable, easier to modify and less scary. To me it has the pro that one could easily wrap common used decorators in a single iterable.

Tim Graham

unread,
Aug 6, 2015, 8:03:50 AM8/6/15
to Django developers (Contributions to Django itself)
A few days ago, we added the ability to use @method_decorator at the class level:

https://github.com/django/django/commit/3bdaaf6777804d33ee46cdb5a889b8cc544a91f1


Does it help?

Is your proposal to add the reduce() syntax to the docs?

Fabrizio Messina

unread,
Aug 7, 2015, 4:01:23 AM8/7/15
to Django developers (Contributions to Django itself)
No my point is that the proposed solution:

https://docs.djangoproject.com/en/1.8/topics/class-based-views/intro/#decorating-in-urlconf

will couple logic inside the urlconf and the methodology itself tend to create hard to read code.

Still I think it makes sense to choose to wrap the Klass.as_view() over creating mixin or using method_decorators mostly because you can add logic to a class without actually alter its definition.

I think it would be better to have a small function in utils that accepts an iterator of functions and a class with an as_view() class method in order to do something similar to what I do with the reduce:

from django.utils.decorators import decorate_class_view
awesome_view
= decorate_class_view(KlassView, list_of_decorators)

Alternatively I propose to change the documentation and add the reduce syntax or an equivalent, and not advise to decorate inside the Urlconf.

Fabrizio Messina

unread,
Aug 7, 2015, 2:22:51 PM8/7/15
to Django developers (Contributions to Django itself)
Sorry did read the commit code now and the new method_decorator seems to solve most of the problem

Fabrizio Messina

unread,
Aug 11, 2015, 5:53:55 AM8/11/15
to Django developers (Contributions to Django itself)
Sorry to keep it alive, but what about the ability to pass an iterator to the method_decorator? It would be sightly similar to this:

        def _wrapper(self, *args, **kwargs):
           
def bound_func(*args2, **kwargs2):
               
return func.__get__(self, type(self))(*args2, **kwargs2)
           
if hasattr(decorator, "__iter__"):
                bound_func
= reduce(lambda acc, dec: dec(acc), decorator, bound_func)
           
else:
                bound_func
= decorator(bound_func)
           
# 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)

The discussion is about the usefulness of this I think.
Reply all
Reply to author
Forward
0 new messages