I have some users that are allowed to see a certain view.
To allow users to login and complain with a 403 Forbidden
for those users that cannot see that login, I can use the following (as explained here):
@permission_required('polls.can_vote', raise_exception=True)
@login_required
def my_view(request):
...
This indeed works as expected. But all my views are class-based views. Since Django 1.9 (finally!) there are a bunch of pretty mixins for doing things that were only possible through the decorators. However...
class MyClassView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
raise_exception = <???>
permission_required = 'polls.can_vote'
template_name = 'poll_vote.html'
this doesn't work. Because the raise_exception
flag is used by both LoginRequiredMixin
and PermissionRequiredMixin
, I cannot set it to anything.
raise_exception
is True
, a user that is not logged in receives a 403 Forbidden
(which I do not want).raise_exception
is False
, a user that is not allowed to see the view, will be redirected to the login page which, because the user is logged in, will redirect again to the page. Creating a not-at-all fancy redirect loop.Of course I could implement my own mixin that behaves I expected, but is there any Django-way of doing this in the view itself? (not in the urls.py
)
The desired behavior is the default since 2.1, so the other answers are obsolete:
Changed in 2.1: In older versions, authenticated users who lacked permissions were redirected to the login page (which resulted in a loop) instead of receiving an HTTP 403 Forbidden response. [src]
Simplest solution seems to be a custom view mixin. Something like that:
class PermissionsMixin(PermissionRequiredMixin):
def handle_no_permission(self):
self.raise_exception = self.request.user.is_authenticated()
return super(PermissionsMixin, self).handle_no_permission()
Or, just use PermissionRequiredMixin
as usual and put this handle_no_premission
to every CBV.
I wanted to add a comment, but my reputation does not allow. How about the following? I feel the below is more readable?
Updated after comments
My reasoning is: You basically write modified dispatch
from LoginRequiredMixin
and just set raise_exception = True
. PermissionRequiredMixin
will raise PermissionDenied
when correct permissions are not met
class LoggedInPermissionsMixin(PermissionRequiredMixin):
raise_exception = True
def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated():
return redirect_to_login(self.request.get_full_path(),
self.get_login_url(),
self.get_redirect_field_name())
return super(LoggedInPermissionsMixin, self).dispatch(request, *args, **kwargs)
For many cases raising 403 for unauthenticated users is the expected behaviour. So yes, you need a custom mixin:
class LoggedInPermissionsMixin(PermissionRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated():
return redirect_to_login(self.request.get_full_path(),
self.get_login_url(), self.get_redirect_field_name())
if not self.has_permission():
# We could also use "return self.handle_no_permission()" here
raise PermissionDenied(self.get_permission_denied_message())
return super(LoggedInPermissionsMixin, self).dispatch(request, *args, **kwargs)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With