How to use Python’s HTTPStatus with Django
A “magic number” is the anti-pattern of using a number directly rather than storing it in a descriptive variable name. In web code HTTP status codes are often used as magic numbers, perhaps because web developers memorize common codes such as 200 and 404. In Python, we can avoid such magic with descriptive references from the standard library’s http.HTTPStatus
enum.
Let’s look at two ways to use HTTPStatus
in our Django code.
1. Creating Responses
Django includes a bunch of HttpResponse
subclasses for common status codes, but the list is deliberately non-exhaustive. If we need to return a status code for which a classes does not exist, we can use Python’s HTTPStatus
with Django’s HttpResponse
.
For example, if one of our pages is unavailable due to legal reasons, so we want to return status code 451. We can do this with HttpResponse
like so:
from http import HTTPStatus
from django.http import HttpResponse
def taken_down(request):
...
return HttpResponse(
content,
status_code=HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS,
)
If we find ourselves using a status code a lot in our project, we can create our own HttpResponse
subclasses, as the documentation mentions. For example:
from http import HTTPStatus
from django.http import HttpResponse
class HttpResponseLegallyUnavailable(HttpResponse):
status_code = HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS
Additionally, when using Django’s render()
shortcut, we can change the status code with the status
argument:
from http import HTTPStatus
from django.shortcuts import render
def taken_down(request):
...
return render(
request,
"illegal.html",
status=HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS,
)
2. In Test Assertions
When using Django’s test client, we normally make assertions on the response status codes. Such assertions can also be clarified using HTTPStatus
:
from http import HTTPStatus
from django.test import TestCase
class IndexTests(TestCase):
def test_success(self):
response = self.client.get("/")
self.assertEqual(response.status_code, HTTPStatus.OK)
...
class TakenDownTests(TestCase):
def test_success(self):
response = self.client.get("/some-taken-down-page/")
self.assertEqual(
response.status_code,
HTTPStatus.UNAVAILABLE_FOR_LEGAL_REASONS,
)
...
rest_framework.status
Django REST Framework has a similar construct in its rest_framework.status
module, which predates http.HTTPStatus
. This isn’t an enum but a module containing numerical constants such as HTTP_200_OK
.
I recommend you use http.HTTPStatus
over rest_framework.status
for a few reasons:
- It’s part of the standard library you can use it in any project, not just those using Django REST Framework.
- It has more data: each status code also has its reason phrase and description.
- As an enum it is a separate type to
int
, which is useful for comparison, debugging, and type checking.
Read my book Boost Your Git DX to Git better.
One summary email a week, no spam, I pinky promise.
Related posts: