login_required and new class based views

327 views
Skip to first unread message

Valentin Golev

unread,
Oct 19, 2010, 1:06:25 PM10/19/10
to Django users
Hello,

I'm trying to start using new class based views from the trunk.

I need to rewrite a view which is decorated
django.contrib.auth.decorators.login_required.

How should I go with that?

I was going to write something like LoginRequiredMixin, but I have no
idea how to do this. I need to run my code before .dispatch(), but I
also have to call the old dispatch, but since Mixin aren't inherited
from View, I can't just override method and use super().

Decorating the whole View class doesn't seem like a good idea, either.

Am I missing something?

Thank you very much!

Łukasz Rekucki

unread,
Oct 19, 2010, 3:18:41 PM10/19/10
to django...@googlegroups.com
On 19 October 2010 19:06, Valentin Golev <v.g...@gmail.com> wrote:
> Hello,
>
> I'm trying to start using new class based views from the trunk.
>
> I need to rewrite a view which is decorated
> django.contrib.auth.decorators.login_required.
>
> How should I go with that?
There are couple of options.

1) decorate the final view (for use in urls.py):

decorated_view = login_required(MyView.as_view)

In this option, you lose the ability to subclass the decorated view.

2) decorate the dispatch method. You need to turn login_required into
a method decorator first (Django should probably provide a tool for
this). Here[1] is an example how to do this.

class MyDecoratedView(MyView):

@on_method(login_required):
def dispatch(self, *args, **kwargs):
# do any extra stuff here
return super(MyDecoratedView, self).dispatch(*args, **kwargs)

3) Make a class decorator, that does the above, so you could do:

@on_dispatch(login_required)
class MyDecoratedView(MyView):
pass

>
> I was going to write something like LoginRequiredMixin, but I have no
> idea how to do this. I need to run my code before .dispatch(), but I
> also have to call the old dispatch, but since Mixin aren't inherited
> from View, I can't just override method and use super().

This is option #4. You can just do:

class LoginRequiredMixin(object):

def dispatch(self, *args, **kwargs):
bound_dispatch = super(LoginRequired, self).dispatch
return login_required(bound_dispatch)(*args, **kwargs)


[1]: http://www.toddreed.name/content/django-view-class/

--
Łukasz Rekucki

Valentin Golev

unread,
Oct 19, 2010, 3:21:49 PM10/19/10
to django...@googlegroups.com
Thank you!

Does python's super really works the way like in the last option?

--
Best Regards,
Valentin Golev
Lead Developer
r00, http://r00.ru

http://valyagolev.net
+7 921 789 0895, avaiable 12:00-18:00 MSK

2010/10/19 Łukasz Rekucki <lrek...@gmail.com>:

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

Łukasz Rekucki

unread,
Oct 19, 2010, 3:48:16 PM10/19/10
to django...@googlegroups.com
On 19 October 2010 21:21, Valentin Golev <m...@valyagolev.net> wrote:
> Thank you!
>
> Does python's super really works the way like in the last option?
Yes, it should call dispatch() from the next class in MRO. So if you
place it at the start, like this:

class MyView(LoginRequiredMixin, TemplateView):
pass

It should work as expected.

--
Łukasz Rekucki

Valentin Golev

unread,
Oct 19, 2010, 4:20:35 PM10/19/10
to django...@googlegroups.com
Thank you, I didn't know that!

Russell Keith-Magee

unread,
Oct 20, 2010, 7:12:11 AM10/20/10
to django...@googlegroups.com
2010/10/20 Łukasz Rekucki <lrek...@gmail.com>:

> On 19 October 2010 19:06, Valentin Golev <v.g...@gmail.com> wrote:
>> Hello,
>>
> 2) decorate the dispatch method. You need to turn login_required into
> a method decorator first (Django should probably provide a tool for
> this).

Django does :-) It's called method_decorator.

from django.utils.decorators import method_decorator

class MyDecoratedView(MyView):

@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
#...

This works for any method on any class, that you want to decorate with
any function-based decorator.

As a point of interest, the django.utils.decorators module has a
couple of other useful utilities in this vein, such as
decorator_from_middleware (which, predictably, enables you to turn any
middleware into a decorator that wraps a single view).

Yours,
Russ Magee %-)

Harro

unread,
Oct 20, 2010, 10:44:50 AM10/20/10
to Django users
Let me add that the decorator_from_middleware call all the middleware
process_* functions if available but these will ofcourse be called at
a whole different point in time then the actual middleware.
Normally middleware is woven through the whole dispatch process, as a
decorator it's wrapped around the view.

On Oct 20, 1:12 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
> 2010/10/20 Łukasz Rekucki <lreku...@gmail.com>:

Joachim Pileborg

unread,
Oct 23, 2010, 2:08:13 AM10/23/10
to Django users

On 19 Okt, 21:18, Łukasz Rekucki <lreku...@gmail.com> wrote:
> On 19 October 2010 19:06, Valentin Golev <v.go...@gmail.com> wrote:> Hello,
> > I was going to write something like LoginRequiredMixin, but I have no
> > idea how to do this. I need to run my code before .dispatch(), but I
> > also have to call the old dispatch, but since Mixin aren't inherited
> > from View, I can't just override method and use super().
>
> This is option #4. You can just do:
>
> class LoginRequiredMixin(object):
>
> def dispatch(self, *args, **kwargs):
> bound_dispatch = super(LoginRequired, self).dispatch
> return login_required(bound_dispatch)(*args, **kwargs)

This solution looks cleanest, and is easiest to implement. However
there is
a problem if two or more similar mixins are used: The order of the
calls are
dependant on the order the view inherits the mixins. If you make a
misstake
in the inheritance-order, your mixin might not be called at all, which
might
not always be whats intended.

I am personally working on option #3, with a simple linked list of
callables
that calls each other until the original "get", "post", etc. is
called.

--
Joachim Pileborg

Łukasz Rekucki

unread,
Oct 23, 2010, 9:03:54 AM10/23/10
to django...@googlegroups.com
On 23 October 2010 08:08, Joachim Pileborg <joachim....@gmail.com> wrote:
>
> On 19 Okt, 21:18, Łukasz Rekucki <lreku...@gmail.com> wrote:
>> On 19 October 2010 19:06, Valentin Golev <v.go...@gmail.com> wrote:> Hello,
>> > I was going to write something like LoginRequiredMixin, but I have no
>> > idea how to do this. I need to run my code before .dispatch(), but I
>> > also have to call the old dispatch, but since Mixin aren't inherited
>> > from View, I can't just override method and use super().
>>
>> This is option #4. You can just do:
>>
>> class LoginRequiredMixin(object):
>>
>>     def dispatch(self, *args, **kwargs):
>>         bound_dispatch = super(LoginRequired, self).dispatch
>>         return login_required(bound_dispatch)(*args, **kwargs)

This discussion has moved to django-developers. It starts in [1] and
then continued in [2]. There is also a ticket #14512 for tracking this
issue[3]. All help is welcome :)

>
> This solution looks cleanest, and is easiest to implement. However
> there is
> a problem if two or more similar mixins are used: The order of the
> calls are
> dependant on the order the view inherits the mixins.

Yes it is. But I don't really see a problem with this. That's how
Python works. Wrapping the mixin construction to a class decorator
should make this a bit more obvious in what order the decorators will
be applied. With the code above, to wrap your view "MyView" with
a login_required you would need to do something like this:

class MyProtectedView(LoginRequiredMixin, MyView):
pass

> If you make a misstake
> in the inheritance-order, your mixin might not be called at all, which
> might not always be whats intended.

If all classes you inherit from play nicely (that means they call
super() in dispatch()), they your mixin will always be called. It
might be
called *earlier* then you expect, but not later or not at all.

>
> I am personally working on option #3, with a simple linked list of
> callables
> that calls each other until the original "get", "post", etc. is
> called.

See the links. There are some problems with decorators that leave
attributes (like csrf_protect) to consider. I will be more than happy
to work toghether.


[1]: http://groups.google.com/group/django-developers/browse_frm/thread/f4bad32127776177
[2]: http://groups.google.com/group/django-developers/browse_frm/thread/f36447f96277fe8c
[3]: http://code.djangoproject.com/ticket/14512

--
Łukasz Rekucki

Reply all
Reply to author
Forward
0 new messages