Interactive, self-contained ER-diagram HTML and Excel table definitions from a live MySQL or PostgreSQL database — single file, zero required dependencies
Project description
erdscope
Generate a self-contained, interactive ER diagram — and an Excel table-definition workbook — from a live MySQL or PostgreSQL database, with a single-file, zero-dependency Python CLI.
pip install erdscope
erdscope mysql://readonly@127.0.0.1:3306/myapp_production -o erd.html
erdscope postgres://readonly@127.0.0.1:5432/myapp_production -o erd.html
The database is the source of truth (tables, columns, comments, indexes, real foreign
keys). Application code (Rails / Prisma / Django) can optionally be layered on top to
add association semantics the database cannot express (has_many :through,
polymorphic, ...).
Demo
Try the live demo → — a small e-commerce schema with comments, indexes, real FKs and an inferred relation. Everything below is one self-contained HTML file.
Regenerate it anytime with python3 docs/gen_demo.py.
Read the user manual → — installation, CLI/config reference, a full viewer guide, and troubleshooting. (日本語版 →)
Install & usage
pip install erdscope (or pipx install erdscope) gives you the erdscope command.
Prefer not to install anything? erd.py is a single, dependency-free file — grab it
and run it with any Python 3.9+:
curl -O https://raw.githubusercontent.com/orapli/erdscope/main/erd.py
python3 erd.py ... # identical to the erdscope command below
erdscope mysql://readonly@127.0.0.1:3306/myapp_production -o erd.html
# PostgreSQL: same thing (schema defaults to public; override with ?schema=name)
erdscope postgres://readonly@127.0.0.1:5432/myapp_production -o erd.html
# enrich with association semantics parsed from application code (optional)
erdscope mysql://readonly@127.0.0.1:3306/myapp_production \
--models /path/to/rails/app -o erd.html
# also write a table-definition workbook
erdscope mysql://readonly@127.0.0.1:3306/myapp_production \
--excel table_definitions.xlsx -o erd.html
Behind a bastion? Open an SSH tunnel first and point at localhost:
ssh -N -L 3307:db-host:3306 bastion &
erdscope mysql://readonly@127.0.0.1:3307/myapp_production -o erd.html
Use a read-only account, and leave the password out of the URL — if it's not in
MYSQL_PWD either, you'll be prompted for it (hidden input, never touches argv or
shell history). See Dependencies below for how the DB connection
itself is made.
Options
| Option | Description |
|---|---|
-o FILE |
Output HTML path (default: erd.html) |
--models PATH |
Merge associations parsed from code: a Rails project (or app/models dir), a schema.prisma, or a Django project — auto-detected |
--excel FILE.xlsx |
Also write a table-definition workbook: an overview sheet plus one sheet per table (columns, defaults, keys, comments, indexes, associations) |
--excel-template FILE.xlsx |
Override the workbook's colors/fonts/borders from a template .xlsx — see excel-template.xlsx and its Styles sheet for the 5-cell contract (default: built-in styling) |
--max-rows N |
Max column rows shown per table (default: 15; the rest scroll) |
--only 'user*,post*' |
Include only tables matching the glob pattern(s) |
--exclude '*_logs' |
Exclude tables matching the glob pattern(s) |
--infer-fk |
Guess relations from *_id column names when no real association/FK backs them (off by default — see below) |
--table-map 'Widget=crm_widgets' |
Rails only: override a model's table when static analysis can't determine it (e.g. table_name set inside a concern that lives in a gem, not the app). Repeatable; comma-separated lists accepted |
--config PATH |
Load defaults from a config file instead of repeating flags — see below. Auto-discovered as .erdscope.json/.yml/.yaml in the current directory if not given |
--no-config |
Skip config auto-discovery even if .erdscope.* exists in the cwd |
Config file
Once the flag list above gets long, put it in a config file instead — .erdscope.json
(or .yml/.yaml with PyYAML) next to where you run the tool is picked up automatically.
Most keys mirror a CLI option (an explicit flag always wins over the config); the DB
connection is config-only as engine/host/port/user/database — deliberately with
no password field — and relations manually declares relations no FK, code, or inference
can find. See the Config file chapter of the manual for the full key
list and semantics, and erdscope.example.yml for a fully
annotated sample based on the live demo's schema.
Dependencies
erd.py runs with zero required dependencies — everything below is optional, and
the tool degrades gracefully (falls back, or fails with a clear message) when a piece
is missing. If you installed via pip, extras pull them in for you:
pip install 'erdscope[mysql]' (PyMySQL), 'erdscope[postgres]' (psycopg),
'erdscope[yaml]' (PyYAML), or 'erdscope[all]'.
| Library | Used for | If not installed |
|---|---|---|
| PyMySQL | MySQL connections | Falls back to shelling out to the mysql CLI (must be on PATH) |
| psycopg (or psycopg2) | PostgreSQL connections | Falls back to shelling out to the psql CLI (must be on PATH) |
| PyYAML | Reading a .yml/.yaml config file |
A .json config still works with no dependency; pointing --config/auto-discovery at a .yml/.yaml file without PyYAML installed exits with a clear error |
Excel output (--excel) needs none of these — it's written directly via the stdlib
zipfile/XML, not a spreadsheet library.
Test-only, and only if you run that particular suite:
| Library | Used for |
|---|---|
| openpyxl | Roundtrip-verifying --excel output in the unit tests (tests/test_erd.py); that one test skips itself if it's missing |
| Playwright | The browser E2E suite (tests/test_e2e.py) — see Tests below |
What you get
Feature highlights — each link goes to the relevant manual chapter:
- Database truth — tables, columns (full SQL types, defaults, extras), table and
column comments, indexes, and real FK constraints, read from the database catalog
(
information_schemaon MySQL,pg_catalogon PostgreSQL) - Code semantics on top —
--modelsmerges Rails / Prisma / Django associations; declared, DB-FK, and inferred edges stay visually distinct - Interactive exploration — focus with depth and dependency direction, two-level hiding, table and column search (with regex/case toggles), a non-filtering Highlight search that survives into exports, named views, share links
- Readable layouts — viewport-aware packing with crossing reduction, drag-to-snap with guide lines, multi-select align/distribute, Auto-tidy, layout undo/redo
- Exports — PNG (2x), SVG, Mermaid, and PlantUML, each with its own
copy and download buttons and image options, plus the Excel workbook
(customizable via
--excel-template) - Logical names — a table's DB comment doubles as a searchable
logical name (e.g.
users(Customer accounts)), with independent display modes for the live view and exports - Extras — a built-in
?shortcuts/help popup, dark mode, print stylesheet, resizable/collapsible panes
Tests
python3 -m unittest discover -s tests -v
The IR builders and the Excel writer are covered by pure unit tests; the overlay
parsers have minimal fixtures under tests/fixture_*. No database is required to
run the tests.
tests/test_e2e.py drives the generated HTML's client-side JS (grid layout,
multi-select align/distribute, drag-to-snap, Auto-tidy) in a real headless browser. It's optional and skips
itself if not set up:
pip install playwright && playwright install chromium
python3 -m unittest tests.test_e2e -v
Extending
Everything downstream (UI, layouts, exports) consumes the intermediate representation
documented at the top of erd.py. Adding another database engine means adding one
parser that produces that shape — parse_postgres() (which reuses the MySQL
adapter's IR builder wholesale) is the working example of the pattern.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file erdscope-0.2.0.tar.gz.
File metadata
- Download URL: erdscope-0.2.0.tar.gz
- Upload date:
- Size: 124.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53745184620c0261ccf14d9a5de82ddb0ce28af32ff3a3c17bba770501f2c770
|
|
| MD5 |
eb78392dd62fcebb5474ee22a6177c84
|
|
| BLAKE2b-256 |
6bc98d13e4c404b658316c9140d6414b78e40cd31a54af0aa5a0f6ceaa1bca71
|
Provenance
The following attestation bundles were made for erdscope-0.2.0.tar.gz:
Publisher:
release.yml on orapli/erdscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
erdscope-0.2.0.tar.gz -
Subject digest:
53745184620c0261ccf14d9a5de82ddb0ce28af32ff3a3c17bba770501f2c770 - Sigstore transparency entry: 2136199587
- Sigstore integration time:
-
Permalink:
orapli/erdscope@881a0cb334094b397e02f3fb618694e2e6c9a143 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/orapli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@881a0cb334094b397e02f3fb618694e2e6c9a143 -
Trigger Event:
push
-
Statement type:
File details
Details for the file erdscope-0.2.0-py3-none-any.whl.
File metadata
- Download URL: erdscope-0.2.0-py3-none-any.whl
- Upload date:
- Size: 84.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
988ad468865518670c52efd745d7de33bb1c287845d3db40c326afbc9b6539e2
|
|
| MD5 |
19a1ee2613e9642442b719aed31e47fc
|
|
| BLAKE2b-256 |
c9470055fba2ab4ddf531652e333bb449f32b6bec95dbfb1e6b81ddf4e5aacab
|
Provenance
The following attestation bundles were made for erdscope-0.2.0-py3-none-any.whl:
Publisher:
release.yml on orapli/erdscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
erdscope-0.2.0-py3-none-any.whl -
Subject digest:
988ad468865518670c52efd745d7de33bb1c287845d3db40c326afbc9b6539e2 - Sigstore transparency entry: 2136199673
- Sigstore integration time:
-
Permalink:
orapli/erdscope@881a0cb334094b397e02f3fb618694e2e6c9a143 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/orapli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@881a0cb334094b397e02f3fb618694e2e6c9a143 -
Trigger Event:
push
-
Statement type: