Opened 13 years ago

Closed 13 years ago

Last modified 8 years ago

#16626 closed New feature (worksforme)

login_required attribute in class-based Generic Views

Reported by: szczav Owned by: nobody
Component: Generic views Version: dev
Severity: Normal Keywords: login_required decorator view generic views
Cc: szczav@… Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Current generic views implementation allows for using login_required decorator in two ways:

  1. By using decorator in urls:
        login_required(MyView.as_view())
    
  2. By overriding dispatch method:
       @method_decorator(login_required)
       def dispatch(self, request, *args, **kwargs):
           return super(self.__class__, self).dispatch(request, *args, **kwargs)
    

First way doesn't allow for subclassing decorated view and is ugly because separates part of class logic into separate file. Second way is also ugly - it makes you override dispatch() method in every place where you want to use one, simple decorator. It's especially annoying on websites which have a lot of content visible only for signed users. It's unpleasant to read and violates DRY. The best solution to this problem is creating login_required class attribute for all generic views. It'd work for all generic views this way:

    class MyView(TemplateView):
        login_required = True
        template_name = 'some_template.html'

Patch attached.

Attachments (1)

login_required.diff (1.3 KB ) - added by szczav 13 years ago.

Download all attachments as: .zip

Change History (5)

by szczav, 13 years ago

Attachment: login_required.diff added

comment:1 by Aymeric Augustin, 13 years ago

Resolution: worksforme
Status: newclosed

I'm not in favor of tying the generic views to the auth framework. If we add a special case for login_required, the next question is obviously going to be about permission_required('foo.bar'). Clearly, it isn't desireable to duplicate the auth API in the generic views.

What about defining your view like this:

class MyView(...):
    # this is a generic view
    ...

my_view = login_required(MyView.as_view())

and using my_view in your URLconf?

If you have many generic views that should be decorated with login required, an alternative is to implement a mixin, like this:

class LoginRequiredMixin(object):
    def as_view(cls):
        return login_required(super(LoginRequiredMixin, cls).as_view())

class MyView(LoginRequiredMixin, ...):
    # this is a generic view
    ...

and use MyView.as_view() is your URLconf.

comment:2 by xhannan, 12 years ago

I have been trying to implement the LoginRequiredMixin class the way it is shown above, but running into some issues. Here is the error message I get --

"unbound method as_view() must be called with HomeView instance as first argument (got nothing instead)"

I have implemented (rather copied and pasted) the LoginRequiredMixin class. Here is the code --

from django.contrib.auth.decorators import login_required
from django.views.generic import TemplateView

class LoginRequiredMixin(object):
    def as_view(cls):
        return login_required(super(LoginRequiredMixin, cls).as_view())

class HomeView(LoginRequiredMixin, TemplateView):
    template_name = 'web/home.html'

Here is my urls.py file --

from django.conf.urls import patterns, url
from web.views import *

urlpatterns = patterns('web',
                       url(r'^$', HomeView.as_view(), name='web-home'),
                       url(r'^login/$', LoginView.as_view(), name='web-login'),
)

Here is the complete error message on browser --

TypeError at /web/
unbound method as_view() must be called with HomeView instance as first argument (got nothing instead)
Request Method:	GET
Request URL:	http://127.0.0.1:8000/web/
Django Version:	1.5.1
Exception Type:	TypeError
Exception Value:	
unbound method as_view() must be called with HomeView instance as first argument (got nothing instead)
Exception Location:	/Users/khan/PycharmProjects/DjangoTest/web/urls.py in <module>, line 5
Python Executable:	/Users/khan/virtuanenvs/django-test-env/bin/python
Python Version:	2.7.4
Python Path:	
['/Users/khan/PycharmProjects/DjangoTest',
 '/Applications/PyCharm.app/helpers/pydev',
 '/Users/khan/PycharmProjects/DjangoTest',
 '/Users/khan/virtuanenvs/django-test-env/lib/python27.zip',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/plat-darwin',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/plat-mac',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/plat-mac/lib-scriptpackages',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/lib-tk',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/lib-old',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/lib-dynload',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
 '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/site-packages',
 '/Users/khan/virtuanenvs/django-test-env/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info']
Server time:	Mon, 22 Apr 2013 04:55:05 -0400

What did I miss? Please help! Thanks a bunch.

comment:3 by Christoph Heer, 12 years ago

With the @classmethod decorader the code example works fine.

from django.contrib.auth.decorators import login_required

class LoginRequiredMixin(object):

    @classmethod
    def as_view(cls):
        return login_required(super(LoginRequiredMixin, cls).as_view())

I aggree with aaugustin that a mixin is the better solution instead a attribute in the generic view but maybe it is possible to put this mixin in django.contrib.auth.

comment:4 by StephenChan, 8 years ago

For anyone else stumbling upon this ticket years later, three mixins including LoginRequiredMixin were added by #24914 and made available in Django 1.9. Docs here.

Note: See TracTickets for help on using tickets.
Back to Top