django-joist
A live database structure viewer for Django. Joist introspects your live schema and renders it as a scrollable, zoomable ER diagram right inside your app, so you can see how tables actually connect without opening a DB client. It reads structure only — tables, columns, keys, indexes, foreign keys; row data is never queried or exposed.
django-joist is a Django port of the concept implemented by Laravel Truss (MIT, Alberto Arena), which inspired it. The name, branding and Django wiring are original; selected front-end code is adapted under MIT with attribution.
Features
- Live ER diagram of your database, rendered with Mermaid — self-hosted, no CDN, no build step.
- Focus mode: a table and its foreign-key neighbours, with a searchable picker that stays usable on schemas with hundreds of tables.
- Filter by table name; toggle native DB types against Django-style labels.
- Map-style pan and zoom, auto-fit with a legible floor, plus a Fit button.
- Exports: PNG/SVG from the browser; DBML, JSON, CSV, Markdown data
dictionary, Mermaid, or a token-trimmed
llmformat — from the dashboard or from CI withmanage.py joist_export. Deterministic: the same schema always produces the same bytes, sojoist_export --checkfails the build when a committed schema file drifts. - Feed your real structure to a coding agent as grounding context:
annotations for business meaning,
--compactto trim tokens,--focusto narrow to one neighbourhood. Only the schema is read — row data is never touched. - Schema diff: what changed since the last migration, in the dashboard's
Changes panel and via
manage.py joist_diff. - Schema doctor:
manage.py joist_doctorreviews the structure for problems (missing primary keys, unindexed foreign keys, duplicate indexes, risky types) — deterministic, structure-only, safe in CI, with--fail-onexit codes; the same findings power the dashboard's Health panel. - Multiple databases: list aliases under
JOIST["connections"]and switch diagrams from the toolbar, each scoped to its own database. - Light/dark "blueprint" theme, or bring your own: a handful of semantic colour/font knobs re-skin the whole dashboard, served as a same-origin stylesheet (CSP-safe, no inline styles).
- Rebuilds itself: the cached snapshot refreshes after every
migrateand degrades gracefully when the cache store is unreachable. - Production-safe by design: routes answer 404 unless enabled, denials never confirm the dashboard exists, and excluded tables are filtered server-side so their structure never reaches the browser.
Installation
pip install django-joist
Add the app and mount the dashboard:
# settings.py
INSTALLED_APPS = [
...,
"django_joist",
]
# urls.py
from django.urls import include, path
urlpatterns = [
...,
path("joist/", include("django_joist.urls")),
]
Requires Python 3.13+ and Django 5.2+. No migrations, no models, no
collectstatic needed (assets are served by the package, gated with the
rest of the dashboard). The dashboard HTML is a template shipped inside the
app, so your TEMPLATES must keep "APP_DIRS": True for django_joist
(the default in django-admin startproject); if you disabled it, add the
package's template directory to DIRS.
Quick start
By default Joist is enabled when DEBUG = True. Start your dev server and
visit /joist/.
To run Joist gated on staging or production, set both switches:
JOIST = {
"enabled": True,
"authorization": {
"allowed_emails": ["ada@example.com", "grace@example.com"],
},
}
Outside DEBUG the shipped default authorizer admits only allow-listed
emails and fails closed on an empty list. To authorize by role instead,
point authorization.callable at your own request -> bool (a callable or a
dotted path); it fully replaces the default. Denials always return 404.
Your project's
AuthenticationMiddlewaremust run for the mounted URLs (the normal case). Joist's own guard is part of the views and cannot be configured away.
Command line
python manage.py joist_show # the structure as a terminal table
python manage.py joist_open # print/open the dashboard URL
python manage.py joist_rebuild # rebuild the cached snapshot (CI)
python manage.py joist_diff # what changed since the last migration
python manage.py joist_doctor # structure review; exits non-zero on findings
python manage.py joist_export # DBML to stdout by default
python manage.py joist_export --format=json --output=docs/schema.json
python manage.py joist_export --format=dbml --output=docs/schema.dbml --check # CI drift check
python manage.py joist_export --tables=orders,order_lines --focus=orders --depth=1 --compact
joist_doctor --format=json and --fail-on=warning make it a CI gate;
joist_export --check regenerates, compares, writes nothing and exits 1 on
drift (2 on usage errors), so a migration that changes the schema without
refreshing the committed file fails the build.
As AI context
The same export doubles as grounding context for a coding agent, so it stops inventing columns:
python manage.py joist_export --format=llm # dense, token-trimmed plaintext
python manage.py joist_export --compact # drop defaults and non-unique indexes
python manage.py joist_export --focus=orders --depth=1
Annotations add the business meaning a type cannot — declare them in
settings under JOIST["annotations"] (per-table, per-column, global notes)
or let Joist read native database comments (Postgres/MySQL) by keeping
"database" in annotations["source"]. The llm format and Markdown/DBML
all render annotations; --no-annotations strips them.
Programmatically, the builder is immutable and mirrors the CLI byte-for-byte:
from django_joist import snapshot
text = (
snapshot()
.only(["orders", "order_lines"])
.focus("orders", depth=1)
.compact()
.to_dbml()
)
Configuration
Everything lives in one JOIST dict; unset keys keep their defaults (nested
dicts merge key-by-key). The common knobs:
| Key | Purpose |
|---|---|
enabled |
Master switch; defaults to DEBUG. Off = every route 404s |
connections |
Visualizable database aliases + per-alias overrides, e.g. {"analytics": {"excluded_tables": [...]}}; empty = just default |
excluded_tables |
Hidden server-side (never sent to the browser). Django bookkeeping tables are pre-listed |
authorization.callable / .allowed_emails |
Who may view outside DEBUG; a custom callable replaces the email allow-list |
cache.ttl / cache.alias |
Snapshot cache (seconds, <= 0 = forever) and which CACHES entry to use |
diagram.type_labels |
native (default) or django short labels |
diagram.mermaid_url |
Point at your own copy/CDN; default self-hosts from the gated asset route |
focus.default_depth |
FK-neighbour hops shown on focus |
large_schema.warn_above |
Table count that triggers the "use focus/filter" hint |
diff.enabled / diff.dir |
Schema-diff switch and where the structure-only baseline lives (default BASE_DIR/var/joist) |
doctor.* |
Preset, per-rule severity/enable, ignore patterns, fail_on, dashboard panel switches |
annotations.* |
Global notes, per-table/per-column meaning, comment sources |
theme.* |
Semantic colour/font knobs for both light and dark |
base_url |
Used by joist_open to build a browsable URL |
The baseline file under diff.dir is the only thing Joist writes to disk;
it is derived and safe to delete — worth gitignoring alongside var/.
The no-data promise
Structure is the CREATE TABLE definition: tables, columns (name, native
type, nullability, defaults), primary keys, indexes, foreign keys with
referential actions, and native comments. Row contents are never queried —
not by the dashboard, the API, the CLI, or the doctor. Config
excluded_tables are stripped server-side, so an excluded table never
reaches the client, the diff, or an export.
Development
pip install -e ".[dev]"
python -m pytest
License
MIT. See LICENSE (includes the attribution to Laravel Truss).
Vendored assets carry their own licenses (Mermaid MIT, IBM Plex Mono SIL OFL)
under src/django_joist/static/joist/.
Metadata
Release files for django-joist 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_joist-0.1.0.tar.gz | 1.1 MB | With provenance | Details for django_joist-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_joist-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for django_joist-0.1.0-py3-none-any.whl |
Total release size: 2.3 MB
Release files / django_joist-0.1.0.tar.gz
| Download URL | django_joist-0.1.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
edd7909c28429aa65f92141063b14981d37f3eddac2ddd4c94ecf6c7d9082aa6
|
|
BLAKE2b-256 checksum How to use checksums |
7cd57e26077118d0e4acd0a7e47e0214f2da6c3f1a75efdcf119aec47dcf6fe6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 11, 2026.
Transparency logRelease files / django_joist-0.1.0-py3-none-any.whl
| Download URL | django_joist-0.1.0-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c2a209fff1c02add3b6dbbea65765bbbb07d806f07f13565cfab94868236938
|
|
BLAKE2b-256 checksum How to use checksums |
e3064af0df6c9e8b4fedd019fcdc913cb995f9210d90a2dcc5177b91b16a87e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 11, 2026.
Transparency logSecurity for django-joist 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_joist-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 Sep 11, 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.