How to use Python’s HTTPStatus with Django2021-06-30
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
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
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.
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
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
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, ) ...
Django REST Framework has a similar construct in its
rest_framework.status module, which predates
This isn’t an enum but a module containing numerical constants such as
I recommend you use
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.
May your code’s only magic be joyous,
🎉 My book Speed Up Your Django Tests is now up to date for Django 3.2. 🎉
Buy now on Gumroad
One summary email a week, no spam, I pinky promise.
Tags: django, python
© 2021 All rights reserved.