Django: implement HTTP basic authentication
Previously, we covered bearer authentication within HTTP’s general authentication framework. In this post, we’ll implement basic authentication, where the client provides a username and password.
Previously, we covered bearer authentication within HTTP’s general authentication framework. In this post, we’ll implement basic authentication, where the client provides a username and password.
Django 6.0 was released today, starting another release cycle for the loved and long-lived Python web framework (now 20 years old!). It comes with a mosaic of new features, contributed to by many, some of which I am happy to have helped with. Below is my pick of highlights from the release notes.
HTTP has a general authentication framework that defines a pattern into which various authentication schemes can fit. Clients may provide an authorization request header that contains a credential. If authorization is missing or invalid, the server may respond with a 401 (Unauthorized) status code, including a www-authenticate header advertising what authentication schemes are supported. Otherwise, the server can respond with the authenticated resource.
gh, the official GitHub CLIgh (pronounced… “guh”?) is GitHub’s official command line interface (CLI) tool. It lets you interact with GitHub directly from the command line, supporting most key features across more than 25 commands and over 200 subcommands. It’s also great for bridging the terminal-browser gap, allowing you to jump to pages relevant to your current context, such as the PR for the current branch.
Here are some Django-related deals for this year’s Black Friday (28th November) and Cyber Monday (1st December), including my own.
My new book, Boost Your GitHub DX is out now!
HTTP supports response compression, which can significantly reduce the size of responses, thereby decreasing bandwidth usage and load times for users. It’s a cheap and valuable technique for improving website performance.
runserver autoreloadingDjango’s runserver automatically reloads when you change Python files. Without this autoreloading feature, you’d need to manually restart the server every time you made a code change.
git cherry-pick allows you to copy a commit another branch to your current one. A common use case is copying a bug fix from a long-running feature branch to another one, so that it can be merged sooner, for example:
To check if a commit SHA exists on a given branch, you have a couple of options.
SyntaxWarning: 'return' in a 'finally' blockTake this code:
When testing code that outputs to the terminal through either standard out (stdout) or standard error (stderr), you might want to capture that output and make assertions on it. To do so, use contextlib.redirect_stdout() and contextlib.redirect_stderr() to redirect the respective output streams to in-memory buffers that you can then inspect and assert on.
When you want to count the number of files within a Git repository, it's generally best to use Git itself for the task because it will skip over any generated or downloaded files. Here is a command chain to use to count all committed files within the current directory:
Here’s a little tip based on some recent work. The project has a URL pattern where the first part of the URL matches the current role the user is viewing the site as. Let’s say the roles are “chef”, “gourmand”, and “foodie”—example URLs might look like this:
importlib.metadata.version()Sometimes, it’s useful to branch the behaviour of your code based on the version of a package that you have installed. This may be to support an upgrade in your project, or for your own package to support different versions of a dependency.
ModelAdmin.get_queryset() by viewWithin Django’s popular admin site, you can override ModelAdmin.get_queryset() to customize the queryset used by the admin views. It’s often used for performance optimizations, such as adding a select_related() call to batch-fetch related objects:
I’ve found it useful, on occasion, to iterate through all registered URL patterns in a Django project. Sometimes this has been for checking URL layouts or auditing which views are registered.
Recently, I was working in a new repository and found the git blame output often pointed back to a large repository-wide formatting commit (applying Black to all Python files). To ignore this commit in git blame, I added its SHA to a .git-blame-ignore-revs file, per this documentation:
BrokenPipeError when piping output to other commandsIf you’ve written a Python script that outputs a lot of data, then piped that output into another command that only reads part of it, you might have encountered a BrokenPipeError. For example, take this script:
git fast-exportTypically, we share repositories through a Git host, like GitHub, allowing others to clone the repository to get a copy. But sometimes that’s not an option, for example when trying to get someone started on your project when corporate onboarding processes take days to grant GitHub access.
From Django 5.2 (April 2025), the runserver management command outputs a warning:
I recently released a new package called inline-snapshot-django. It’s a tool for snapshot testing SQL queries in Django projects, described shortly.
A neat testing pattern is writing common tests in a base class and then applying them to multiple objects through subclassing. Doing so can help you test smarter and cover more code with less boilerplate.
Python comes with two built-in profilers for measuring the performance of your code: cProfile and profile. They have the same API, but cProfile is a C extension, while profile is implemented in Python. You nearly always want to use cProfile, as it’s faster and doesn’t skew measurements as much.
Git supports storing symbolic links (symlinks) in your repository, which are filesystem links that point to another file or directory. These are great for when you want to have the same file at multiple locations in your repository, such as some configuration file that is used for multiple sub-projects. But how can you list all the symlinks in your repository?