Question

I have internal account privacy permissions in my project(e.g. only friends can see profile page of user) and I want to have custom permission denied page for this case. Is there any way to return response from TemplateView with status code equals 403?

Something like this:

class PrivacyDeniedView(TempateView):
    template_name = '...'
    status_code = 403

I can do this by override dispatch() but maybe Django has out of the box solution

Answer: it looks like there no generic solution. The best way is proposed by @alecxe, but encapsulated in Mixin as @FoxMaSk proposed

Était-ce utile?

La solution

One option is to override get() method of your TemplateView class:

def get(self, request, *args, **kwargs):
    context = self.get_context_data(**kwargs)
    return self.render_to_response(context, status=403)

Autres conseils

You can subclass TemplateResponse and set response_class in the view. For example:

from django.template.response import TemplateResponse

class TemplateResponseForbidden(TemplateResponse):
    status_code = 403

class PrivacyDeniedView(TemplateView):
    response_class = TemplateResponseForbidden
    ...

This approach is more DRY than the other suggestions because you don't need to copy and paste any code from TemplateView (e.g. the call to render_to_string()).

I tested this in Django 1.6.

While alecxe's answer works, I strongly suggest you to avoid overriding get; it's easy to forget that CBV's can have other methods like post, and if you're overriding one you should do the same for the others.

In fact, there is no need to create a separate view just to display a 403 error; Django already has django.http.HttpResponseForbidden. So instead of redirecting to your view, just do something along the lines of:

if not user.has_permission(): # or however you check the permission
    return HttpResponseForbidden()

Or, if you want to render a particular template:

if not user.has_permission(): # or however you check the permission
    return HttpResponseForbidden(loader.render_to_string("403.html"))

I just encountered this problem as well. My goal was to be able to specify the status code in urls.py, e.g.:

url(r'^login/error/?$', TemplateView.as_view(template_name='auth/login_error.html', status=503), name='login_error'),

So using the previous answers in this thread as idea starters, I came up with the following solution:

class TemplateView(django.views.generic.TemplateView):

    status = 200

    def render_to_response(self, context, **response_kwargs):

        response_kwargs['status'] = self.status

        return super(TemplateView, self).render_to_response(context, **response_kwargs)

This is an old question, but I came across it first when searching. I was using a different generic view (ListView) whose implementation for get is a bit more complex, and thus a bit of a mess to override. I went for this solution instead:

def get(self, request, *args, **kwargs):
    response = super(ListView, self).get(request, *args, **kwargs)
    response.status_code = 403
    return response

This also allowed me to decide the status code based on some parameters of the request, which was necessary in my case to perform a 301 redirect for old-style URL patterns.

If you don't require the ability to change status code from within the method, then cjerdonek's answer is ideal.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top