Skip to main content

djangomap

Turn any Django project into a beautiful, interactive architecture diagram.

Models · Views · URLs · Celery tasks · Beat schedules · Signals · Middleware · Admin · Serializers

License: MIT Python 3.9+ Django Zero dependencies

Output: single HTML file Works offline Responsive Static analysis: AST Health checks: 16

Why · Quick start · The three tabs · CLI · CI · How it works · License

djangomap Apps view

Why

You join a Django codebase with 40 apps. Which view serves /checkout/? What fires capture_payment? Which models does the admin actually expose? Answering these means grepping across dozens of files.

djangomap reads your project without importing it and produces a single HTML file that answers those questions visually — plus it flags N+1 risks, circular app dependencies and missing related_names along the way.

Zero setup No database, no DJANGO_SETTINGS_MODULE, no installing the target project's dependencies
Zero dependencies Pure Python standard library — ast and nothing else
Fully offline Tailwind is pre-compiled and inlined; the page makes no network requests
One file Email it, commit it, attach it to a PR, open it from disk
Responsive Works on desktop, tablet and phone

Quick start

pip install -e ./djangomap
djangomap /path/to/your/project -o diagram.html
open diagram.html

That's it. For the full experience, add source links:

djangomap . \
  --title "My Shop" \
  --editor vscode \
  --repo-url https://github.com/me/myshop \
  --branch main \
  -o diagram.html

Or use it as a library:

from djangomap import scan_project, render_html
from djangomap.analysis import analyse

proj = scan_project("/path/to/project")
data = proj.to_dict()
data["health"] = analyse(proj)

render_html(data, "diagram.html")
print(proj.stats())        # {'model': 8, 'view': 8, 'url': 12, ...}

The three tabs

1 · Apps — what is in each app

Every app becomes its own board with a dedicated colour. Inside, cards are grouped by kind and show a useful summary line: a model's field count, a URL's handler and name=, a view's base class, a task's argument signature, a beat entry's schedule.

Relationship wires are routed through the gutters between boards, so they never cut across cards — including relations that span apps.

Apps overview

Click any card to open the detail panel: full field list with on_delete and related_name, the Meta class, properties, methods, cyclomatic complexity, detected issues, and every relation as a clickable link so you can walk the graph.

Detail panel

Selecting a node also dims everything unrelated, leaving just its neighbourhood lit:

Focus mode

Drag a board header to rearrange · scroll to zoom · F to fit · Esc to clear · collapse individual boards or all at once for a bird's-eye view


2 · Flow — how the system fits together

Four architecture views, switchable from the toolbar.

Request Lifecycle

The full path of an HTTP request through parallel lanes: Middleware → URLconf → View → Serializer/Form → Model, with a separate lane for async Celery work.

Request lifecycle

App Dependencies

Which apps import which, derived from real import statements, annotated with reference counts. Circular dependencies show up immediately.

App dependencies

Celery Pipeline

Producers (views, signals, beat entries, management commands) → Broker → Workers → Result backend, including task-to-task chains.

Celery pipeline

Data Model

A full ERD: every model with its fields, and the FK / M2M / O2O relations between them, grouped by app.

ERD

3 · Health — what is wrong

A 0–100 score plus issues grouped by check. Click any issue to jump straight to that card in the Apps tab. Cards with problems get a coloured dot.

Health tab

Below the issue list, a per-app breakdown shows where the debt is concentrated:

Per-app health

Checks

Check Severity Meaning
view.n_plus_one 🔴 error Model has FKs but the view never calls select_related/prefetch_related
url.no_view 🔴 error URL is not wired to any known view
app.cycle 🔴 error Circular dependency between apps
complexity.high 🟡 warn Cyclomatic complexity ≥ 10 (error at ≥ 18)
model.no_str 🟡 warn Model has no __str__, so it renders as Object (1) in admin
model.no_ordering 🟡 warn Used in a ListView but has no Meta.ordering → unstable pagination
url.no_name 🟡 warn No name=, so it cannot be used with reverse()
view.no_perm 🟡 warn DRF view without permission_classes
task.orphan 🟡 warn Task is never called — no delay() and no beat schedule
signal.no_sender 🟡 warn @receiver without sender fires for every model
model.fk_no_related 🔵 info ForeignKey without related_name
model.orphan 🔵 info No view / serializer / admin references this model
model.no_indexes 🔵 info Many fields but no db_index anywhere
task.no_retry 🔵 info No max_retries / autoretry_for
task.no_bind_retry 🔵 info Uses self.retry but not declared with bind=True
view.unauth 🔵 info No login_required / permission mixin

view.unauth and model.orphan are heuristic and can be noisy on real projects, which is why they are info and barely affect the score.


Responsive

The whole UI adapts down to a 320px phone.

Mobile Mobile drawer Mobile detail sheet
Breakpoint Behaviour
≥ 1536px Kind chips inline in the header
< 1536px Kind filters move into the drawer via the Kinds button
≥ 1024px Sidebar always visible
< 1024px Sidebar becomes an off-canvas drawer behind ☰ ; detail panel becomes a bottom sheet and diagrams auto-fit above it
< 760px Flow layers stack vertically and fit to width

Pinch-to-zoom and touch panning work on both canvases. Tablet reflows to two columns:

Tablet

CLI reference

djangomap [path] [options]
Option Description
path Project root (default: .)
-o, --out FILE Output HTML file (default: djangomap.html)
--title TEXT Diagram title
--json FILE Also dump the raw graph as JSON
--editor NAME vscode · vscode-insiders · pycharm · none
--repo-url URL e.g. https://github.com/me/proj — enables "view on remote" links
--branch NAME Branch for --repo-url (default: main)
--format FMT Print a text diagram: mermaid-erd · mermaid-flow · dot
--markdown FILE Write a Markdown report with an embedded Mermaid ERD
--fail-on LEVEL Exit 1 if issues at error / warn / info exist
--no-health Skip the health analysis

Text exports

Paste straight into a README or PR comment:

djangomap . --format mermaid-erd      # GitHub renders this natively
djangomap . --format mermaid-flow
djangomap . --format dot | dot -Tsvg -o graph.svg
djangomap . --markdown report.md
Example Mermaid output
erDiagram
    Product {
        Char title
        Decimal price
        ForeignKey category FK
        ManyToMany tags FK
    }
    Product ||--o{ Category : "category"
    Product }o--o{ Tag : "tags"

CI integration

Fail the build when architectural errors appear:

# .github/workflows/architecture.yml
name: Architecture
on: [push, pull_request]

jobs:
  djangomap:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -e ./djangomap
      - run: djangomap . --fail-on error -o diagram.html
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: architecture-diagram
          path: diagram.html

Every PR then carries a downloadable, up-to-date diagram of the codebase.


Keyboard shortcuts & permalinks

Key Action
1 2 3 Switch to Apps / Flow / Health
F Fit the current view
Esc Clear the selection

The active tab, flow view, selected node and every filter are encoded in the URL #hash. Send the link to a teammate and they land on exactly the same view.


What gets extracted

Models

Every field with its type, max_length, null / blank / unique / db_index / primary_key, on_delete, related_name, default, help_text, whether it has choices — plus the Meta class, @property methods, custom managers and cyclomatic complexity.

Views

CBV and FBV, HTTP methods (from get/post methods or @api_view), model, queryset, serializer_class, form_class, template_name, permission_classes, authentication_classes, paginate_by, lookup_field, filter_backends, and the list of URLs that reach the view.

URLs

path() / re_path() / url() / include(), path converters like <int:pk>, name=, extra kwargs, and DRF router register() calls.

Celery

@shared_task, @app.task and method-level tasks with their full argument signature and options (bind, max_retries, queue, rate_limit, autoretry_for, acks_late, time_limit, …), the delay() / apply_async() call chain, and every CELERY_BEAT_SCHEDULE entry wired to its task.

Everything else

Signals (type + sender), middleware, management commands, forms, serializers (Meta.model, fields, read_only_fields), admin classes (list_display, list_filter, search_fields, registrations), and settings: INSTALLED_APPS, MIDDLEWARE, AUTH_USER_MODEL, ROOT_URLCONF, broker and result backend.

Relationship types

Wire Meaning
fk m2m o2o Model relations
routes URL → View
calls task.delay() / apply_async()
schedules Beat entry → task
uses View → serializer / form / model
queries ORM access inside a view or task
manages Admin class → model
listens Signal receiver → sender model
inherits Class inheritance within the project

How it works

scanner.py    walks the tree, parses each .py with ast, emits nodes + edges
analysis.py   runs 16 checks over that graph, scores the project
export.py     renders Mermaid / DOT / Markdown
render.py     inlines the graph JSON + pre-built Tailwind into one HTML file
cli.py        argument parsing and orchestration

Because everything is AST-based, your code is never executed — safe to point at an unfamiliar repository. The trade-off is that dynamically constructed URLconfs or programmatically generated models won't be seen. For those, the graph is a very good approximation rather than a perfect runtime reflection.

Migrations, node_modules, virtualenvs, caches and static dirs are skipped.


Development

python -m djangomap.cli ./sample_shop -o demo.html    # run against the bundled sample

The Tailwind stylesheet is pre-built at djangomap/tailwind.css. Rebuild it after editing template.html:

npx tailwindcss -c tailwind.config.js -i tw.css -o djangomap/tailwind.css --minify

The repo ships a sample_shop/ Django project (4 apps, 8 models, 6 tasks, DRF, Celery Beat, a management command) used for the screenshots above.


Limitations

  • Dynamic URLconfs and runtime-generated models are invisible to static analysis
  • Third-party apps are only mapped if they live inside the scanned tree
  • related_name reverse accessors that Django creates implicitly are not inferred
  • The N+1 check is a heuristic: it flags missing select_related but cannot know whether the template actually traverses the relation

License

Released under the MIT License — free for personal and commercial use.

Copyright (c) 2026 djangomap contributors

Built with Python's ast, Tailwind CSS and hand-rolled SVG. No runtime dependencies.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

djangomap-0.1.0.tar.gz (49.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

djangomap-0.1.0-py3-none-any.whl (46.0 kB view details)

Uploaded Python 3

File details

Details for the file djangomap-0.1.0.tar.gz.

File metadata

  • Download URL: djangomap-0.1.0.tar.gz
  • Upload date:
  • Size: 49.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for djangomap-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9acb20a3322a9102c3a4361c6a36fe4ed1a3b62ebf3d46185acd2e393302f7c8
MD5 e1e7f4d89db25f151c45f51243a7b5f4
BLAKE2b-256 950934553c2033b4692d6e432c2777f06db0255f156083904d72f20b99e0ac86

See more details on using hashes here.

File details

Details for the file djangomap-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: djangomap-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 46.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for djangomap-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7f46e3a5d6535ef1ac513a87ef1947d67c67c666f4653c41d28a0c13d2680869
MD5 ed13aabfca520fdbc2bb568f12cc2676
BLAKE2b-256 d0532175b46e3496f4d4a99bddc51b590b24360fa24358e31166961242abba90

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page