django-fastresolve
Faster URL resolving for Django, as a drop-in. No changes to your urlconfs.
Django resolves a path by trying every pattern of a urlconf in order, recursing into includes. With a few hundred patterns at one level, every request pays for a few hundred regex searches or string compares, and a 404 pays for all of them.
django-fastresolve indexes the patterns of each resolver on their literal prefix: the text a path must start with for the pattern to have any chance of matching ('projects/' for path('projects/<int:pk>/', ...), 'blog/' for re_path(r'^blog/(?P<slug>\w+)/$', ...)). A path then only tries the patterns whose prefix it starts with, still in their original order. All the matching itself is still Django’s own code: converters, a to_python raising ValueError falling through to later patterns, namespaces, default kwargs.
Installation
pip install django-fastresolve
and add 'django_fastresolve' to INSTALLED_APPS. That’s it.
You can also call django_fastresolve.install() and django_fastresolve.uninstall() yourself.
How much faster?
On a real project with three urlconfs, timing resolve() on every route, with DEBUG = False:
urlconf |
routes |
stock median |
fast median |
stock 404 |
fast 404 |
|---|---|---|---|---|---|
internal app |
785 |
25 µs |
6 µs |
27 µs |
2 µs |
public site |
894 |
64 µs |
4.6 µs |
125 µs |
4.8 µs |
small |
83 |
8 µs |
7 µs |
6 µs |
3 µs |
The more patterns share a level, the bigger the win. Patterns that stock Django already finds in the first couple of tries get about 1 µs slower, from the index lookup.
When does it fall back?
A pattern whose prefix can’t be determined safely gets the empty prefix, which means it is tried for every path, exactly where stock Django would try it. That is the case for:
lazily translated routes (path(gettext_lazy('about/'), ...))
regexes that aren’t anchored with ^, or that contain |
i18n_patterns (LocalePrefixPattern)
URLPattern/URLResolver subclasses that override resolve()
pattern classes it doesn’t know
Only the leading literal part of a regex counts: r'^ab?c/' has the prefix 'a'.
Differences from stock Django
tried (on Resolver404 and on ResolverMatch.tried) only lists the patterns that were actually tried, not every pattern before the match.
With DEBUG = True a 404 is resolved again the stock way, so the 404 debug page lists every pattern as usual. That also acts as a self check: if stock Django finds a match where django-fastresolve didn’t, you get an AssertionError saying so, instead of a wrong 404. Please report it if that happens.
The index for a resolver is built on first use. If you append to or remove from urlpatterns afterwards the index is rebuilt; replacing an item in place is not noticed.
To resolve the stock way for a block of code, in the current context only:
from django_fastresolve import stock_resolving
with stock_resolving():
match = resolve('/some/path/')
Running the tests
uv run pytest
The tests resolve a urlconf full of edge cases, plus a few thousand fuzzed paths, both ways, and assert identical results.
Metadata
Release files for django-fastresolve 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_fastresolve-0.1.0.tar.gz | 20.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_fastresolve-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.9 kB
Release files / django_fastresolve-0.1.0.tar.gz
| Download URL | django_fastresolve-0.1.0.tar.gz |
|---|---|
| Size | 20.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5452d4621eab9e29d0d210d3c539a3ca36b2a03d1ee76d2ba63e0f5870548fc5
|
|
BLAKE2b-256 checksum How to use checksums |
ff7de0023899bf408262daafdfaa4b53631896203a696a3400b6e027863b367d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / django_fastresolve-0.1.0-py3-none-any.whl
| Download URL | django_fastresolve-0.1.0-py3-none-any.whl |
|---|---|
| Size | 7.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
25938083d44e19a3c9daac55438536e1ff6714ee83fd13c22994e95d438a557a
|
|
BLAKE2b-256 checksum How to use checksums |
dda79be7a81cd95409f18bcfc3b515551ce0f3636931c9ad026b69845e494e78
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|