django-visualize-urls
Map every URL of a Django project with the access control of each route, as a standalone HTML page.
show_urls of django-extensions lists the routes of a project. This map arranges them as a tree of includes and
tells, for each route, what protects it: login_required, a permission, a mixin, DRF permission classes, the admin
site, LoginRequiredMiddleware... or nothing. The page has filter tiles, a search field and a meter on each
include, and it needs no network access.
Installation
pip install django-visualize-urls
Add the app to the settings of your development environment:
INSTALLED_APPS = [
# ...
"django_visualize_urls",
]
Usage
python manage.py visualize_urls
writes url-map.html in the current folder. Open it in a browser.
| Option | Effect |
|---|---|
--format html|json |
Standalone page (default), or the same data as JSON for scripts. |
--output PATH, -o PATH |
File to write (default url-map.html or url-map.json); - writes to the standard output. |
--urlconf DOTTED.PATH |
URLconf to map instead of ROOT_URLCONF. |
--editor-root PATH |
Project root to put in the editor links, when the project runs in a container and the editor on the host. |
For a project that runs in a container, the host shell expands $PWD before the command enters the container:
docker compose exec web python manage.py visualize_urls --editor-root "$PWD"
The map reads the URL resolver loaded in memory: it serves no request and sends no query to the database. The page lists the internals of the project (routes, views, file paths): keep it out of public places.
What the map recognizes
Each route falls in one of three categories.
- Protected: an access control the map recognizes covers every method of the route.
- Hint in code: no recognized control, but the code of the view, or of a decorator or mixin of the project,
holds words such as
is_authenticatedorPermissionDenied. A control that covers some handlers only (method_decorator(login_required)onpost()) is a hint too. A hint is to be read, not a proof. - No hint: neither. A public page, or a control done elsewhere (a middleware, a template, a function that the view calls).
Recognized controls:
login_required,permission_required(with its permissions),staff_member_required,user_passes_test, and the decorators of the project built on it, named after their variable:staff_required = user_passes_test(lambda u: u.is_staff)shows asstaff_required.method_decorator()ondispatch(), or on a single handler.LoginRequiredMixin,PermissionRequiredMixin(with its permissions),UserPassesTestMixin.- Django REST framework:
permission_classesother thanAllowAny, compositions such asIsAuthenticated & ~IsAdminUser,@api_viewfunctions, the methods of routed ViewSet actions. - The admin site: the views it serves require an active staff account, custom views wrapped with
admin_view()included; its login page is public. LoginRequiredMiddleware, or a subclass of it: every route except the views markedlogin_not_required(DRF marks its views so).
The map also shows the methods a route answers (require_POST, require_http_methods, the handlers of a
class-based view), the routes exempt from CSRF checks (csrf_exempt, and DRF views, which APIView.as_view()
exempts), the names shared by several routes, the routes without a name, and the other decorators (never_cache,
cache_page...).
A decorator is named after the code of the wrapper it leaves in the __wrapped__ chain: functools.wraps copies
the name of the view onto the wrapper, but not its __code__. Its arguments, such as the test of
user_passes_test, are read from the closure of the wrapper.
Settings
All keys are optional:
VISUALIZE_URLS = {
"HINTS": ["check_api_key("],
"NEUTRAL_DECORATORS": ["require_htmx"],
"ACCESS_DECORATORS": ["shop.decorators.partner_required"],
"EDITOR_URL": "vscode://file/{path}:{line}",
}
| Key | Default | Meaning |
|---|---|---|
HINTS |
[] |
Strings that reveal a hand-written check in project code, added to the built-in words. For a check done in a function the view calls (a webhook signature), list that function: "verify_signature(". |
NEUTRAL_DECORATORS |
[] |
Decorators that do not control access: their code is not searched for hints. For a decorator that answers 403 for another reason, such as a request that HTMX did not send. |
ACCESS_DECORATORS |
[] |
Decorators that control access although nothing in their code says so: their routes count as protected. |
EDITOR_URL |
"vscode://file/{path}:{line}" |
Link that opens a project view in an editor. {path} is the absolute path of the file without its leading slash, {line} its line. None removes the links. |
Decorators are listed by name ("require_htmx") or by dotted path ("shop.decorators.require_htmx"). Other
editors: "cursor://file/{path}:{line}", "zed://file/{path}:{line}", "pycharm://open?file=/{path}&line={line}".
Project and third-party routes
A route is the project's when it is declared in a URLconf module of the project (a TemplateView declared in
urls.py counts) or served by a view of the project. Project code lives under settings.BASE_DIR (or, without
it, the folder that holds the package of ROOT_URLCONF) and outside site-packages. A list of patterns has no
module (admin.site.urls, include([...])): its routes are the project's when their view is. The page hides the
third-party routes until you turn on the switch.
Limits
The map reads code, it does not run it. It does not see checks done in a middleware other than
LoginRequiredMiddleware, in a template, in an override of get_permissions() or has_permission(), or in a
function the view calls; the hints and the settings cover part of it. A view that cannot be read stays on the map,
flagged with the error.
Requirements
Python 3.11 or later, Django 5.2, 6.0 or 6.1. Django REST framework is optional.
Development
uv sync
uv run coverage run -m pytest -m "not browser"
uv run coverage report
uv run ruff format
uv run ruff check
The tests run on a demo shop (tests/demo) with one route for each way of protecting a view, without database or
network. Its map:
uv run django-admin visualize_urls --settings tests.settings --pythonpath .
The test of the page in a browser needs Playwright and Chromium, a large download:
uv sync --group browser
uv run playwright install chromium
uv run pytest -m browser
To release a version, set it (uv version --bump minor), move the changes of CHANGELOG.md under it, push, then
publish a GitHub release whose tag is the version:
gh release create v0.2.0 --generate-notes
The publish workflow checks that the tag matches the version, builds the package, smoke-tests the wheel and the source distribution, then uploads them to PyPI with Trusted Publishing and PEP 740 attestations.
License
MIT
Metadata
Release files for django-visualize-urls 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 | Provenance | Actions |
|---|---|---|---|---|
| django_visualize_urls-0.1.0.tar.gz | 40.3 kB | With provenance | Details for django_visualize_urls-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_visualize_urls-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for django_visualize_urls-0.1.0-py3-none-any.whl |
Total release size: 67.6 kB
Release files / django_visualize_urls-0.1.0.tar.gz
| Download URL | django_visualize_urls-0.1.0.tar.gz |
|---|---|
| Size | 40.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b3453e5bf7acf7a036afbfd9de2e51be037d5d1b011418175f918eedec119c43
|
|
BLAKE2b-256 checksum How to use checksums |
e8460b65d5a8a4ccb463f4be48feea9c13106ba774f747b85c163a7ae5b78a00
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logRelease files / django_visualize_urls-0.1.0-py3-none-any.whl
| Download URL | django_visualize_urls-0.1.0-py3-none-any.whl |
|---|---|
| Size | 27.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c728a7eedb726537a00da0d47d2eb1f4b7fd7a9cc4407eda61d75128445b3713
|
|
BLAKE2b-256 checksum How to use checksums |
9f8c784ebdd18c88dce5c98b76203827175972b97f7decd49e8e41a9295c17c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logSecurity for django-visualize-urls 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for django_visualize_urls-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.