#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 , 13 years ago
Attachment: | login_required.diff added |
---|
comment:1 by , 13 years ago
Resolution: | → worksforme |
---|---|
Status: | new → closed |
comment:2 by , 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 , 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:
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:
and use
MyView.as_view()
is your URLconf.