#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:
- By using decorator in urls:
login_required(MyView.as_view())
- 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)
Change History (5)
by , 14 years ago
| Attachment: | login_required.diff added |
|---|
comment:1 by , 14 years ago
| Resolution: | → worksforme |
|---|---|
| Status: | new → closed |
comment:2 by , 13 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 , 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.
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 aboutpermission_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_viewin 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.