Django 3.0 introduced a convenient way of handling error views, namely handler400, handler403, handler404, and handler500.
As an example from Django, you can simply do this in your project's urls.py
.
# urls.py
handler404 = 'mysite.views.my_custom_page_not_found_view'
handler500 = 'mysite.views.my_custom_error_view'
handler403 = 'mysite.views.my_custom_permission_denied_view'
handler400 = 'mysite.views.my_custom_bad_request_view'
However, these handlers act globally. That means if you have 5 different apps in your project, they will share the same error page. This is not a very good side effect.
We should make all apps independent from each other. So how should we really do it?
The proper way
For example we have 2 apps (or more) named blog
and demo
and we have error endpoints on each app called blog:error
and demo:error
, we can introduce a function directly inside the project urls.py
.
# urls.py
def bad_request_handler(request: WSGIRequest, exception=None):
if request.path.startswith("/blog/"):
return redirect("blog:error", error_code=400)
elif request.path.startswith("/demo/"):
return redirect("demo:error", error_code=400)
return defaults.bad_request(request, exception) # use the default handler
handler400 = bad_request_handler
Notice that I made a redirect here to the error pages. You can also render the templates directly but I choose this way because I do not want to import the views from every app in my urls.py
. This always depend on the developer's choice.
Continuing on all the remaining handlers...
# urls.py
def bad_request_handler(request: WSGIRequest, exception=None):
if request.path.startswith("/blog/"):
return redirect("blog:error", error_code=400)
elif request.path.startswith("/demo/"):
return redirect("demo:error", error_code=400)
return defaults.bad_request(request, exception)
def permission_denied_handler(request: WSGIRequest, exception=None):
if request.path.startswith("/blog/"):
return redirect("blog:error", error_code=403)
elif request.path.startswith("/demo/"):
return redirect("demo:error", error_code=403)
return defaults.permission_denied(request, exception)
def page_not_found_handler(request: WSGIRequest, exception=None):
if request.path.startswith("/blog/"):
return redirect("blog:error", error_code=404)
elif request.path.startswith("/demo/"):
return redirect("demo:error", error_code=404)
return defaults.page_not_found(request, exception)
def server_error_handler(request: WSGIRequest):
if request.path.startswith("/blog/"):
return redirect("blog:error", error_code=500)
elif request.path.startswith("/demo/"):
return redirect("demo:error", error_code=500)
return defaults.server_error(request)
handler400 = bad_request_handler
handler403 = permission_denied_handler
handler404 = page_not_found_handler
handler500 = server_error_handler
All handlers except 500
are the same. The defaults.server_error()
only accept a request parameter. You can check the documentation for the error views from here.