Django Guide

Django architecture diagram: the deployed system, not just the models

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

The Django diagram ecosystem is almost entirely ERDs. graph_models from django-extensions, djangoviz, and the rest all take models.py and draw entities and relations. Useful, and a solved problem. The picture nobody generates is the one an on-call engineer needs: gunicorn behind which proxy, Celery with which broker, which cache, which storage, which cron beat, and what ships it.

6 min readFor Django teams where the deployment picture lives in one head

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

178/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Migrations are the schema history the selector can read

The classifier picks up Python files under a directory named migrations, which is exactly where Django puts them. That class is also the only one sampled from both ends of the sorted list: with its default ceiling of six, three come from the earliest files and three from the most recent. Worth knowing before the first sync: choosing the architecture focus drops that ceiling to two, one from each end, because under that view the deployment is the subject and the schema is context.

On a Django app that pairing is unusually informative. 0001_initial.py holds the shape the project started with. The most recent numbered migrations hold what the team has been changing this quarter. Everything between them is mostly the path from one to the other, and reading all of it would consume the corpus without changing the diagram.

pyproject.toml is classified as the dependency manifest, and that is where the rest of the stack announces itself: celery, redis, psycopg, boto3, django-storages, sentry. Those lines become external nodes. A requirements.txt is not one of the manifest names the selector reads, so a project that keeps its dependencies only there gives the generator less to work with.

Django repo, what reaches the corpus
  docker-compose.yml                 containers class
  Dockerfile                         containers class
  deploy/k8s/*.yaml                  manifests class
  apps/orders/migrations/0001_*.py   migration class (from the start)
  apps/orders/migrations/0087_*.py   migration class (from the end)
  pyproject.toml                     dependency manifest class
  .github/workflows/deploy.yml       CI class

Skipped before the walk
  __pycache__/  .venv/  venv/  poetry.lock  uv.lock

The runtime picture comes from compose, manifests, and CI

A Django repository almost always carries a compose file for local work and either a set of deployment manifests or a CI pipeline for production. Between them they declare the process list, and the process list is the architecture: web, worker, beat, and whatever else has its own command.

The environment blocks in those files carry the edges. DATABASE_URL points at Postgres, CELERY_BROKER_URL at Redis or RabbitMQ, the storage settings at S3 or Azure Blob, and the mail and error reporting variables at their providers. Those become labelled connections rather than assumed ones.

What comes out is a deployment diagram with real icons: the proxy, the WSGI or ASGI process, the worker and beat, the database, the cache, the broker, the bucket, and the pipeline that builds and releases them. That is the diagram people go looking for at two in the morning, and the one that is always missing.

Rails repositories, honestly

A Rails repository produces the same kind of deployment diagram from the same classes: compose file, Dockerfiles, deployment manifests under a cluster-named directory, CI workflows, and the README. Those are usually enough for the runtime view, which is the view this page is about.

The schema side is thinner, and for two reasons rather than one. The migration class needs a directory named exactly migrations and an extension of .sql, .py, .js or .ts, so Ruby files under db/migrate miss on both counts. And Gemfile is not among the six dependency manifest names the selector reads, which are package.json, pyproject.toml, go.mod, pom.xml, Cargo.toml and composer.json. The generator sees those directories and their file counts in the tree summary but does not read their contents.

The practical approach for a Rails app: let the sync draw the deployed system from the containers and pipelines, and generate the domain model separately from a description or from the SQL your schema dump produces.

What an ERD tool does better

For entity relationships out of models.py, django-extensions graph_models is the right tool and always will be. This is the complementary view. See ERD from SQL when the schema itself is the subject.

Once connected

Read-only access to GitHub, GitLab, or Azure DevOps, a branch or a tag, and a daily sync that skips unchanged commits. Nothing is executed: no manage.py, no settings import, no database connection, which is what makes it safe to point at a production repository.

The sync also writes an architecture doc from the same corpus, covering the components, the infrastructure, and the build and deploy steps. Put the diagram in the runbook or the README as a live image so the version people read is the version that is deployed.

FAQ

Does it read models.py?

No. Python files are classified only under a directory named migrations, so models.py is not fetched. The schema reaches the generator through the migration files instead, sampled from the earliest and the most recent, which together show the initial shape and the current direction of change.

Why sample migrations from both ends?

Because the two ends carry the information. The first migrations describe the schema the project started with, the latest describe what is changing now, and the hundreds in between mostly describe the path from one to the other. Sampling both ends keeps the file budget available for the containers and pipelines.

What about a Rails repository?

The runtime view works the same way, from the compose file, Dockerfiles, deployment manifests, and CI pipelines. The schema side does not: Ruby migration files under db/migrate are outside the migration class, and Gemfile is not one of the dependency manifests the selector reads, so those show up only as directory counts.

Do I need a requirements.txt or a pyproject.toml?

pyproject.toml is the Python dependency manifest the selector classifies, and it is where the external services in the diagram usually come from. A project that lists dependencies only in requirements.txt gives the generator less to identify providers with, though the compose file and CI pipeline still contribute.

Is anything executed against the project?

Nothing. No manage.py command runs, no settings module is imported, no dependency is installed, and no database is contacted. The sync reads files from the repository over the provider API with read-only access and works from their text, which is what makes it safe to point at a production repository and usable on a branch that has never been deployed.