Skip to content

CLI & Python API

erdify ships a command-line interface, a module entry point, and a Python API for programmatic use.

CLI Options

usage: erdify [-h] [-o OUTPUT] [--title TITLE] [--exclude [PATTERN ...]]
              [--exclude-paths [PATTERN ...]] [--no-default-excludes]
              [--sources [KIND ...]] [--include PATTERN [PATTERN ...]]
              [--sql-dialect NAME] [--infer-keys] [--django-raw-types]
              [--no-enums] [--no-relationships] [--format FMT [FMT ...]]
              [--inject FILE] [--check] [-v]
              input

Generate PlantUML ERD diagrams from SQLModel, SQLAlchemy, Django, Pydantic and
dataclass models

positional arguments:
  input                 Directory or file with models (.py) or SQL DDL (.sql)

options:
  -h, --help            show this help message and exit
  -o, --output OUTPUT   Output .puml file (default: stdout)
  --title TITLE         Diagram title (default: 'Database ERD')
  --exclude [PATTERN ...]
                        Glob patterns (case-sensitive) to exclude entities by
                        class name or table name, e.g. --exclude '*Link'
                        audit_log
  --exclude-paths [PATTERN ...]
                        Glob patterns (case-sensitive) for models.py files to
                        skip before parsing, matched against the path relative
                        to input or any path segment, e.g. --exclude-paths
                        migrations legacy 'apps/experimental/*'
  --no-default-excludes
                        Do not auto-skip models.py under venv/site-
                        packages/cache dirs (.venv, site-packages,
                        __pycache__, ...); scan them too
  --sources [KIND ...]  Restrict which model kinds become entities. Choices:
                        sqlmodel, sqlalchemy, django, dataclass, pydantic.
                        Default: all. e.g. --sources sqlmodel sqlalchemy for
                        DB tables only
  --include PATTERN [PATTERN ...]
                        Glob patterns for files to scan (default: models.py).
                        A pattern with '/' matches the path relative to input
                        ('**' crosses dirs); a pattern without '/' matches a
                        filename at any depth. Replaces the default, so list
                        models.py too if you want it, e.g. --include models.py
                        '**/models/*.py' tables.py
  --sql-dialect NAME    SQL dialect hint for parsing .sql DDL with the [sql]
                        extra (e.g. postgres, mysql, sqlite). Default: a
                        permissive generic read
  --infer-keys          For keyless models (Pydantic/dataclass), infer a
                        primary key from a field named 'id' and a foreign key
                        from '<x>_id' (target table '<x>')
  --django-raw-types    For Django models, show original field names
                        (CharField, TextField) instead of mapped Python types
                        (str, int, datetime)
  --no-enums            Skip enum definitions in output
  --no-relationships    Skip relationship lines in output
  --format FMT [FMT ...]
                        Output format(s): plantuml (.puml), mermaid (.mmd),
                        json (.json), html (.html). Default: plantuml. With -o
                        the extension is set per format; multiple formats
                        require -o, e.g. --format plantuml mermaid
  --inject FILE         Inject the diagram into a markdown file between '<!--
                        erdify:start -->' and '<!-- erdify:end -->' markers
                        (only that region is rewritten). Uses a single
                        --format (default mermaid). Combine with --check to
                        fail on drift, e.g. --inject README.md
  --check               Don't write; exit non-zero if the --output file is
                        missing or differs from the freshly generated diagram
                        (for CI / pre-commit drift checks)
  -v, --version         show program's version number and exit

Example: erdify ./src/database -o database_erd.puml

The block above is generated from erdify --help; run python scripts/gen_cli_docs.py after changing CLI flags.

Configuration via pyproject.toml

Instead of passing flags every time, commit them under [tool.erdify] in your project's pyproject.toml. erdify searches upward from the input directory for the nearest pyproject.toml and reads that table.

[tool.erdify]
title = "My Database Schema"
output = "docs/database_erd"        # relative paths resolve from the project root
format = ["plantuml", "mermaid"]    # one or both
sources = ["django"]
exclude = ["audit_log", "*Link"]
exclude_paths = ["migrations", "legacy"]
infer_keys = true
django_raw_types = false
sql_dialect = "postgres"            # required for CREATE TYPE … AS ENUM support

With that in place, erdify . uses these settings. Precedence is explicit CLI flag > [tool.erdify] value > built-in default. (Boolean flags merge by OR: a flag enabled in config can be added to on the CLI but not turned off there.)

Keeping the diagram in sync (--check)

--check regenerates the diagram in memory and compares it to the existing --output file or the injected region in an --inject target without writing. It exits 0 when the content is up to date and non-zero when the file is missing or stale — ideal for CI or a pre-commit hook.

erdify ./src/database -o docs/erd.puml --check
erdify ./src/database --inject README.md --check

Running as Module

python -m erdify ./src/database -o erd.puml

Python API

from pathlib import Path
from erdify import parse_models_directory, generate_plantuml

# Parse Python models
entities, enums = parse_models_directory(Path("./src/database"))

# Parse SQL DDL (requires erdify[sql])
entities, enums = parse_models_directory(Path("schema.sql"), sql_dialect="postgres")

# Generate PlantUML
diagram = generate_plantuml(
    entities=entities,
    enums=enums,
    title="My Database ERD"
)

# Save or use the diagram
Path("erd.puml").write_text(diagram)

parse_models_directory accepts both a Python models directory and a .sql file (or a directory scanned with --include '*.sql'). For Python models only, the lower-level ASTDatabaseParser class is also available — it does not handle .sql input, so use parse_models_directory when working with SQL DDL.

Programmatic Access

For lower-level control, use the parser and generator classes directly:

from erdify import (
    ASTDatabaseParser,
    PlantUMLGenerator,
    EntityInfo,
    FieldInfo,
    EnumInfo,
)

# Low-level parser access (Python models only — use parse_models_directory for .sql)
parser = ASTDatabaseParser(Path("./models"))
entities, enums = parser.parse_all_models()

# Access entity details
for name, entity in entities.items():
    print(f"Table: {entity.table_name}")
    for field in entity.fields:
        if field.is_primary_key:
            print(f"  PK: {field.name}")
        elif field.is_foreign_key:
            print(f"  FK: {field.name} -> {field.foreign_table}")

# Custom generator with options
generator = PlantUMLGenerator(
    entities=entities,
    enums=enums,
    title="Custom ERD"
)
output = generator.generate()