255đź‘Ť
There are a few strategies listed in the CBV docs:
Decorate the view when you instantiate it in your urls.py
(docs)
from django.contrib.auth.decorators import login_required
urlpatterns = [
path('view/',login_required(ViewSpaceIndex.as_view(..)),
...
]
The decorator is applied on a per-instance basis, so you can add it or remove it in different urls.py
routes as needed.
Decorate your class so every instance of your view is wrapped (docs)
There’s two ways to do this:
-
Apply
method_decorator
to your CBV dispatch method e.g.,from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required @method_decorator(login_required, name='dispatch') class ViewSpaceIndex(TemplateView): template_name = 'secret.html'
If you’re using Django < 1.9 (which you shouldn’t, it’s no longer supported) you can’t use method_decorator
on the class, so you have to override the dispatch
method manually:
from django.contrib.auth.decorators import login_required
class ViewSpaceIndex(TemplateView):
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ViewSpaceIndex, self).dispatch(*args, **kwargs)
-
Use a mixin like django.contrib.auth.mixins.LoginRequiredMixin outlined well in the other answers here:
from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
Make sure you place the mixin class first in the inheritance list (so Python’s Method Resolution Order algorithm picks the Right Thing).
The reason you’re getting a TypeError
is explained in the docs:
Note:
method_decorator passes *args and **kwargs as parameters to the decorated method on the class. If your method does not accept a compatible set of parameters it will raise a TypeError exception.
120đź‘Ť
Here is my approach, I create a mixin that is protected (this is kept in my mixin library):
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
class LoginRequiredMixin(object):
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
Whenever you want a view to be protected you just add the appropriate mixin:
class SomeProtectedViewView(LoginRequiredMixin, TemplateView):
template_name = 'index.html'
Just make sure that your mixin is first.
Update: I posted this in way back in 2011, starting with version 1.9 Django now includes this and other useful mixins (AccessMixin, PermissionRequiredMixin, UserPassesTestMixin) as standard!
- [Django]-How to delete a record in Django models?
- [Django]-Django model one foreign key to many tables
- [Django]-How can I disable logging while running unit tests in Python Django?
51đź‘Ť
Here’s an alternative using class based decorators:
from django.utils.decorators import method_decorator
def class_view_decorator(function_decorator):
"""Convert a function based decorator into a class based decorator usable
on class based Views.
Can't subclass the `View` as it breaks inheritance (super in particular),
so we monkey-patch instead.
"""
def simple_decorator(View):
View.dispatch = method_decorator(function_decorator)(View.dispatch)
return View
return simple_decorator
This can then be used simply like this:
@class_view_decorator(login_required)
class MyView(View):
# this view now decorated
- [Django]-Django Rest Framework custom response message
- [Django]-Access web server on VirtualBox/Vagrant machine from host browser?
- [Django]-Django model blank=False does not work?
22đź‘Ť
For those of you who use Django >= 1.9, it’s already included in django.contrib.auth.mixins
as AccessMixin
, LoginRequiredMixin
, PermissionRequiredMixin
and UserPassesTestMixin
.
So to apply LoginRequired to CBV(e.g. DetailView
):
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.detail import DetailView
class ViewSpaceIndex(LoginRequiredMixin, DetailView):
model = Space
template_name = 'spaces/space_index.html'
login_url = '/login/'
redirect_field_name = 'redirect_to'
It’s also good to keep in mind GCBV Mixin order: Mixins must go on the left side, and the base view class must go in the right side. If the order is different you can get broken and unpredictable results.
- [Django]-What is more efficient .objects.filter().exists() or get() wrapped on a try
- [Django]-Pylint "unresolved import" error in Visual Studio Code
- [Django]-Add rich text format functionality to django TextField
14đź‘Ť
I realise this thread is a bit dated, but here’s my two cents anyway.
with the following code:
from django.utils.decorators import method_decorator
from inspect import isfunction
class _cbv_decorate(object):
def __init__(self, dec):
self.dec = method_decorator(dec)
def __call__(self, obj):
obj.dispatch = self.dec(obj.dispatch)
return obj
def patch_view_decorator(dec):
def _conditional(view):
if isfunction(view):
return dec(view)
return _cbv_decorate(dec)(view)
return _conditional
we now have a way to patch a decorator, so it will become multifunctional. This effectively means that when applied to a regular view decorator, like so:
login_required = patch_view_decorator(login_required)
this decorator will still work when used the way it was originally intended:
@login_required
def foo(request):
return HttpResponse('bar')
but will also work properly when used like so:
@login_required
class FooView(DetailView):
model = Foo
This seems to work fine in several cases i’ve recently come across, including this real-world example:
@patch_view_decorator
def ajax_view(view):
def _inner(request, *args, **kwargs):
if request.is_ajax():
return view(request, *args, **kwargs)
else:
raise Http404
return _inner
The ajax_view function is written to modify a (function based) view, so that it raises a 404 error whenever this view is visited by a non ajax call. By simply applying the patch function as a decorator, this decorator is all set to work in class based views as well
- [Django]-Django – filtering on foreign key properties
- [Django]-Django: Multiple forms possible when using FormView?
- [Django]-How to test auto_now_add in django
5đź‘Ť
Use Django Braces. It provides a lot of useful mixins that is easily available.
It has beautiful docs. Try it out.
You can even create your custom mixins.
http://django-braces.readthedocs.org/en/v1.4.0/
Example Code:
from django.views.generic import TemplateView
from braces.views import LoginRequiredMixin
class SomeSecretView(LoginRequiredMixin, TemplateView):
template_name = "path/to/template.html"
#optional
login_url = "/signup/"
redirect_field_name = "hollaback"
raise_exception = True
def get(self, request):
return self.render_to_response({})
- [Django]-How to get getting base_url in django template
- [Django]-__init__() got an unexpected keyword argument 'mimetype'
- [Django]-How to check if ManyToMany field is not empty?
4đź‘Ť
If it’s a site where the majority of pages requires the user to be logged in, you can use a middleware to force login on all views except some who are especially marked.
Pre Django 1.10 middleware.py:
from django.contrib.auth.decorators import login_required
from django.conf import settings
EXEMPT_URL_PREFIXES = getattr(settings, 'LOGIN_EXEMPT_URL_PREFIXES', ())
class LoginRequiredMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
path = request.path
for exempt_url_prefix in EXEMPT_URL_PREFIXES:
if path.startswith(exempt_url_prefix):
return None
is_login_required = getattr(view_func, 'login_required', True)
if not is_login_required:
return None
return login_required(view_func)(request, *view_args, **view_kwargs)
views.py:
def public(request, *args, **kwargs):
...
public.login_required = False
class PublicView(View):
...
public_view = PublicView.as_view()
public_view.login_required = False
Third-party views you don’t want to wrap can be made excempt in the settings:
settings.py:
LOGIN_EXEMPT_URL_PREFIXES = ('/login/', '/reset_password/')
- [Django]-Django rest framework change primary key to use a unqiue field
- [Django]-In Django, how does one filter a QuerySet with dynamic field lookups?
- [Django]-Separation of business logic and data access in django
4đź‘Ť
In my code I have written this adapter to adapt member functions to a non-member function:
from functools import wraps
def method_decorator_adaptor(adapt_to, *decorator_args, **decorator_kwargs):
def decorator_outer(func):
@wraps(func)
def decorator(self, *args, **kwargs):
@adapt_to(*decorator_args, **decorator_kwargs)
def adaptor(*args, **kwargs):
return func(self, *args, **kwargs)
return adaptor(*args, **kwargs)
return decorator
return decorator_outer
You can simply use it like this:
from django.http import HttpResponse
from django.views.generic import View
from django.contrib.auth.decorators import permission_required
from some.where import method_decorator_adaptor
class MyView(View):
@method_decorator_adaptor(permission_required, 'someapp.somepermission')
def get(self, request):
# <view logic>
return HttpResponse('result')
- [Django]-How to understand lazy function in Django utils functional module
- [Django]-Django content-type : how do I get an object?
- [Django]-Iterating over related objects in Django: loop over query set or use one-liner select_related (or prefetch_related)
4đź‘Ť
It has been a while now and now Django has changed so much.
Check here for how to decorate a class-based view.
https://docs.djangoproject.com/en/2.2/topics/class-based-views/intro/#decorating-the-class
The documentation did not include an example of “decorators that takes any argument”. But decorators that take arguments are like this:
def mydec(arg1):
def decorator(func):
def decorated(*args, **kwargs):
return func(*args, **kwargs) + arg1
return decorated
return deocrator
so if we are to use mydec as a “normal” decorator without arguments, we can do this:
mydecorator = mydec(10)
@mydecorator
def myfunc():
return 5
So similarly, to use permission_required
with method_decorator
we can do:
@method_decorator(permission_required("polls.can_vote"), name="dispatch")
class MyView:
def get(self, request):
# ...
- [Django]-How to get a favicon to show up in my django app?
- [Django]-Why does DEBUG=False setting make my django Static Files Access fail?
- [Django]-How to change the name of a Django app?
1đź‘Ť
I’ve made that fix based on Josh’s solution
class LoginRequiredMixin(object):
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(*args, **kwargs)
Sample usage:
class EventsListView(LoginRequiredMixin, ListView):
template_name = "events/list_events.html"
model = Event
- [Django]-Import data from excel spreadsheet to django model
- [Django]-How can I filter a Django query with a list of values?
- [Django]-How can I activate the unaccent extension on an already existing model
1đź‘Ť
This is super easy with django > 1.9 coming with support for PermissionRequiredMixin
and LoginRequiredMixin
Just import from the auth
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
class YourListView(LoginRequiredMixin, Views):
pass
For more details read Authorization in django
- [Django]-Django url tag multiple parameters
- [Django]-How to force Django models to be released from memory
- [Django]-How do you detect a new instance of the model in Django's model.save()
0đź‘Ť
If you are doing a project which requires variety of permission tests, you can inherit this class.
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from django.views.generic import View
from django.utils.decorators import method_decorator
class UserPassesTest(View):
'''
Abstract base class for all views which require permission check.
'''
requires_login = True
requires_superuser = False
login_url = '/login/'
permission_checker = None
# Pass your custom decorator to the 'permission_checker'
# If you have a custom permission test
@method_decorator(self.get_permission())
def dispatch(self, *args, **kwargs):
return super(UserPassesTest, self).dispatch(*args, **kwargs)
def get_permission(self):
'''
Returns the decorator for permission check
'''
if self.permission_checker:
return self.permission_checker
if requires_superuser and not self.requires_login:
raise RuntimeError((
'You have assigned requires_login as False'
'and requires_superuser as True.'
" Don't do that!"
))
elif requires_login and not requires_superuser:
return login_required(login_url=self.login_url)
elif requires_superuser:
return user_passes_test(lambda u:u.is_superuser,
login_url=self.login_url)
else:
return user_passes_test(lambda u:True)
- [Django]-Django-way for building a "News Feed" / "Status update" / "Activity Stream"
- [Django]-Django – getting Error "Reverse for 'detail' with no arguments not found. 1 pattern(s) tried:" when using {% url "music:fav" %}
- [Django]-How can I keep test data after Django tests complete?
0đź‘Ť
Here the solution for permission_required decorator:
class CustomerDetailView(generics.GenericAPIView):
@method_decorator(permission_required('app_name.permission_codename', raise_exception=True))
def post(self, request):
# code...
return True
- [Django]-How to customize activate_url on django-allauth?
- [Django]-What's the idiomatic Python equivalent to Django's 'regroup' template tag?
- [Django]-Do I need Nginx with Gunicorn if I am not serving any static content?