4

How can I redirect a user to the login page when confronted with a 403 Forbidden?

Consider the following code:

urls.py

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
)

views.py

class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserSerializer

        renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)
        template_name='rest_framework/users.html'

        def list(self, request, *args, **kwargs):
            response = super(viewSet, self).list(request, *args, **kwargs)
            if request.accepted_renderer.format == 'html':
                return Response({'request':request, 'queryset': response.data['results']})
            return response

If I visit /domain/users while not logged in, I'll get a 403 Forbidden error. I would like to instead, redirect to the login page. In other words, never show 403 errors but instead redirect directly to the login page.

I have tried putting:

@login_required
def list(...):

but that does not work. I assume because its inside a viewset.

I have also tried:

def list(...):
    if not request.user.is_authenticated():
        return render_to_response('domain/login.html')

but again, doesn't work.

I tried various 403middlewares but to no avail.

I tried redirecting via the handler403 handler in urls.py

handler403 = 'site.views.login'

No success.

What to do?

Django version: 1.5.4

BenMorel
  • 34,448
  • 50
  • 182
  • 322
user3417614
  • 131
  • 2
  • 7

1 Answers1

4

You can't use the standard login_required decorator on a class method. You have to either wrap the as_view() when you use it in the URL:

url(r'...', login_required(UserViewSet.as_view()))

or wrap the decorator with django.utils.decorators.method_decorator and apply it to the class dispatch method:

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class UserViewSet(viewsets.ModelViewSet):
...
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
    ....

(see https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#decorating-class-based-views)

For class-based views you can incorporate this into a reusable PermissionRequiredMixin - see https://stackoverflow.com/a/6455140/839875

Alternatively you can raise a django.core.exceptions.PermissionDenied exception which Django will catch with django.views.defaults.permission_denied

(see https://docs.djangoproject.com/en/1.5/topics/http/views/#the-403-http-forbidden-view)

Community
  • 1
  • 1
ACGray
  • 666
  • 3
  • 10
  • This worked for me. Thank you very much. class UserViewSet(...) @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ProtectedView, self).dispatch(*args, **kwargs) – user3417614 Mar 24 '14 at 16:53
  • There's a neat mixin which you can use for do that too, have added it to my answer – ACGray Mar 24 '14 at 17:04