Creating a custom 404 Page Not Found error page is so easy in Django (all you do is put your own template named “404.html” at the root of your templates directory) that I naturally assumed doing the same for a 403 Forbidden error page would be just as easy. Unfortunately it is not.
After searching around for quite a while last night, I found bits and pieces that I have modified slightly and republished below in a unambiguous step-by-step tutorial (see the “Source and Other Resources” section at the end of the post for a few of the source posts).
The method posted below leverages some custom Django middleware code. Please, if you have a better, more elegant solution I’d love to hear about it in the comments.
Create the Middleware
- Create a directory at the root of your project called “middleware”
- Add a file named “__init__.py” to this directory
- Create a file named “http.py” in this directory with the following contents:
from django.conf import settings
from django.http import HttpResponseForbidden
from django.template import RequestContext,Template,loader,TemplateDoesNotExist
from django.utils.importlib import import_module
"""
# Middleware to allow the display of a 403.html template when a
# 403 error is raised.
"""
class Http403(Exception):
pass
class Http403Middleware(object):
def process_exception(self, request, exception):
from http import Http403
if not isinstance(exception, Http403):
# Return None so django doesn't re-raise the exception
return None
try:
# Handle import error but allow any type error from view
callback = getattr(import_module(settings.ROOT_URLCONF),'handler403')
return callback(request,exception)
except (ImportError,AttributeError):
# Try to get a 403 template
try:
# First look for a user-defined template named "403.html"
t = loader.get_template('403.html')
except TemplateDoesNotExist:
# If a template doesn't exist in the projct, use the following hardcoded template
t = Template("""{% load i18n %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>{% trans "403 ERROR: Access denied" %}</title>
</head>
<body>
<h1>{% trans "Access Denied (403)" %}</h1>
{% trans "We're sorry, but you are not authorized to view this page." %}
</body>
</html>""")
# Now use context and render template
c = RequestContext(request, {
'message': exception.message
})
return HttpResponseForbidden(t.render(c))
- You should now have this:
Modify Your Project’s “settings.py”
- Add ” ‘myproject.middleware.http.Http403Middleware’, ” to your MIDDLEWARE_CLASSES
Create a Custom “403.html” page
- Put it at the root of your template directory
- Sample content: (note: assumes you’ve already defined a “base.html” template)
{% extends "base.html" %}
{% block title %} | Access Denied{% endblock %}
{% block content %}
<h1>Access Denied</h1>
<span>We're sorry, but you are not authorized to view this page (Error: 403)</span>
{% endblock content %}
Raise a 403 Error
- In the file where you want to raise the 403 add this at the top: (I used it in my project’s “view.py” file)
from myproject.middleware.http import Http403
- Raise the 403
if request.user.id != object.user.id:
raise Http403
That’s it, you now have a Django template to handle 403 Forbidden errors. I’m sure there’s a way for your front-end, production web server to do the same, but I haven’t explored that yet.
Source and Other Resources
- A middleware solution is here (HT Felipe ‘chronos’ Prenholato)
- A middleware solution is here (HT Glen Zangirolami)
- A potential decorator solution is here (HT Magus)