Skip to content

db_diagram

write_markdown

write_markdown(
    metadata_source: (
        sqlalchemy.MetaData
        | collections.abc.Iterable[
            sqlalchemy.sql.schema.Table
        ]
        | sqlalchemy.sql.schema.Table
        | str
        | sqlalchemy.URL
        | sqlalchemy.Engine
        | sqlalchemy.Connection
    ),
    path: pathlib.Path | str,
    header_level: int = 2,
    title: str | None = None,
    depth: int = 1,
    image_directory: pathlib.Path | str | None = None,
    image_format: str = "svg",
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
) -> None

Write a markdown document, containing mermaid diagrams, for the specified metadata or tables, to the specified path.

Parameters:

  • metadata_source (sqlalchemy.MetaData | collections.abc.Iterable[sqlalchemy.sql.schema.Table] | sqlalchemy.sql.schema.Table | str | sqlalchemy.URL | sqlalchemy.Engine | sqlalchemy.Connection) –

    SQLAlchemy metadata, tables, or a connection string

  • path (pathlib.Path | str) –

    The path to which to write the markdown document.

  • title (str | None, default: None ) –

    The title of the markdown document. If not specified, the file name (sans extension) will be used as the title.

  • depth (int, default: 1 ) –

    The depth of the relationship graph to include in each diagram

  • include (str | tuple[str, ...], default: () ) –

    Include only tables and views matching the specified pattern(s)

  • exclude (str | tuple[str, ...], default: () ) –

    Exclude tables and views matching the specified pattern(s)

Source code in src/db_diagram/_mermaid.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
def write_markdown(  # noqa: C901
    metadata_source: (
        MetaData | Iterable[Table] | Table | str | URL | Engine | Connection
    ),
    path: Path | str,
    header_level: int = 2,
    title: str | None = None,
    depth: int = 1,
    image_directory: Path | str | None = None,
    image_format: str = "svg",
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
) -> None:
    """
    Write a markdown document, containing mermaid diagrams, for the
    specified metadata or tables, to the specified path.

    Parameters:
        metadata_source: SQLAlchemy metadata, tables, or a connection string
        path: The path to which to write the markdown document.
        title: The title of the markdown document. If not specified,
            the file name (sans extension) will be used as the title.
        depth: The depth of the relationship graph to include in each
            diagram
        include: Include only tables and views matching the
            specified pattern(s)
        exclude: Exclude tables and views matching the specified pattern(s)
    """
    if isinstance(metadata_source, (str, URL, Engine, Connection)):
        metadata_source = tuple(
            get_bind_metadata(metadata_source).tables.values()
        )
    if isinstance(metadata_source, Table):
        metadata_source = (metadata_source,)
    elif not isinstance(metadata_source, MetaData):
        metadata_source = tuple(metadata_source)
    header: str = "#" * header_level
    if isinstance(path, str):
        path = Path(path)
    if isinstance(image_directory, str) and image_directory:
        image_directory = Path(image_directory)
    if image_directory:
        image_directory = cast(Path, image_directory).relative_to(path.parent)
    if not title:
        title = path.stem
    if not path.parent.exists():
        path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", newline="\n") as path_io:
        path_io.write(f"{header[:-1]} {title}\n")
        table_name: str
        mermaid_diagram: str
        tables_diagrams: tuple[tuple[str, str], ...] = (
            iter_tables_mermaid_diagrams(
                metadata_source,
                depth=depth,
                include=include,
                exclude=exclude,
            )
        )
        include_sub_header: bool = len(tables_diagrams) > 1
        for table_name, mermaid_diagram in tables_diagrams:
            if image_directory:
                if TYPE_CHECKING:
                    assert isinstance(image_directory, Path)
                image_path: str = str(
                    image_directory.joinpath(table_name)
                ).replace("\\", "/")
                path_io.write(
                    f"\n{header} {table_name}\n\n"
                    f"![{table_name}]"
                    f"({image_path}"
                    f".mmd.{image_format})\n"
                    if include_sub_header
                    else f"\n![{table_name}]"
                    f"({image_path}"
                    f".mmd.{image_format})\n"
                )
            else:
                path_io.write(
                    f"\n{header} {table_name}\n\n"
                    f"```mermaid\n"
                    f"{mermaid_diagram}\n"
                    "```\n"
                    if include_sub_header
                    else f"\n```mermaid\n{mermaid_diagram}\n```\n"
                )

write_mermaid_images

write_mermaid_images(
    metadata_source: (
        sqlalchemy.MetaData
        | collections.abc.Iterable[
            sqlalchemy.sql.schema.Table
        ]
        | sqlalchemy.sql.schema.Table
        | str
        | sqlalchemy.URL
        | sqlalchemy.Engine
        | sqlalchemy.Connection
    ),
    directory: str | pathlib.Path,
    *,
    image_format: str = "svg",
    depth: int = 1,
    config_file: str | pathlib.Path | None = None,
    background_color: str | None = "transparent",
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
    theme: str | None = None
) -> None

Write images for the specified class(es) in the specified directory.

Parameters:

  • metadata_source (sqlalchemy.MetaData | collections.abc.Iterable[sqlalchemy.sql.schema.Table] | sqlalchemy.sql.schema.Table | str | sqlalchemy.URL | sqlalchemy.Engine | sqlalchemy.Connection) –

    A SQLAlchemy connection URL, engine, connection, or metadata from which to derive schema information

  • directory (str | pathlib.Path) –

    The directory under which to write the images.

  • image_format (str, default: 'svg' ) –

    svg | png

  • config_file (str | pathlib.Path | None, default: None ) –

    A path to a mermaid config file

  • background_color (str | None, default: 'transparent' ) –

    A CSS background color

  • depth (int, default: 1 ) –

    The depth of the relationship graph to include in each diagram

  • include (str | tuple[str, ...], default: () ) –

    Include only tables and views matching the specified pattern(s)

  • exclude (str | tuple[str, ...], default: () ) –

    Exclude tables and views matching the specified pattern(s)

  • theme (str | None, default: None ) –

    default | neutral | dark | forest | base

Source code in src/db_diagram/_mermaid.py
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
def write_mermaid_images(
    metadata_source: (
        MetaData | Iterable[Table] | Table | str | URL | Engine | Connection
    ),
    directory: str | Path,
    *,
    image_format: str = "svg",
    depth: int = 1,
    config_file: str | Path | None = None,
    background_color: str | None = "transparent",
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
    theme: str | None = None,
) -> None:
    """
    Write images for the specified class(es) in the specified directory.

    Parameters:
        metadata_source: A SQLAlchemy connection URL, engine, connection,
            or metadata from which to derive schema information
        directory:
            The directory under which to write the images.
        image_format: svg | png
        config_file: A path to a [mermaid config file](
            https://mermaid.js.org/config/schema-docs/config.html)
        background_color: A CSS background color
        depth: The depth of the relationship graph to include in each
            diagram
        include: Include only tables and views matching the
            specified pattern(s)
        exclude: Exclude tables and views matching the
            specified pattern(s)
        theme: default | neutral | dark | forest | base
    """
    if isinstance(metadata_source, (str, URL, Engine, Connection)):
        metadata_source = tuple(
            get_bind_metadata(metadata_source).tables.values()
        )
    if isinstance(metadata_source, Table):
        metadata_source = (metadata_source,)
    elif not isinstance(metadata_source, MetaData):
        metadata_source = tuple(metadata_source)
    mmdc: tuple[str, ...] = install_mmdc()
    if isinstance(directory, str):
        directory = Path(directory)
    if not config_file:
        config_file = _get_config_file(theme=theme)
    elif isinstance(config_file, Path):
        config_file = str(config_file)
    os.makedirs(directory, exist_ok=True)
    args: tuple[tuple[str, ...], ...]
    arguments: Iterable[tuple[str | Path | tuple | None, ...]] = (
        (*args, directory, config_file, mmdc, background_color, image_format)
        for args in iter_tables_mermaid_diagrams(
            metadata_source,
            depth=depth,
            include=include,
            exclude=exclude,
        )
    )
    max_workers: int | None = None
    if is_ci():
        # If running in a CI environment, limit the number of workers
        # to prevent excessive resource use
        max_workers = 1
    with ProcessPoolExecutor(max_workers=max_workers) as pool:
        deque(pool.map(_write_image, arguments), maxlen=0)

write_mermaid_markdown

write_mermaid_markdown(
    metadata_source: (
        sqlalchemy.MetaData
        | collections.abc.Iterable[
            sqlalchemy.sql.schema.Table
        ]
        | sqlalchemy.sql.schema.Table
        | str
        | sqlalchemy.URL
        | sqlalchemy.Engine
        | sqlalchemy.Connection
    ),
    directory: pathlib.Path | str = "database",
    depth: int = 1,
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
) -> None

Write mermaid markdown documents for the specified base class in the specified directory.

Parameters:

  • metadata_source (sqlalchemy.MetaData | collections.abc.Iterable[sqlalchemy.sql.schema.Table] | sqlalchemy.sql.schema.Table | str | sqlalchemy.URL | sqlalchemy.Engine | sqlalchemy.Connection) –

    A SQLAlchemy database connection, connection string, metadata instance, or tables

  • directory (pathlib.Path | str, default: 'database' ) –

    The directory under which to which to write the mermaid markdown documents.

  • depth (int, default: 1 ) –

    The depth of the relationship graph to include in each diagram

  • include (str | tuple[str, ...], default: () ) –

    Include only tables and views matching the specified pattern(s)

  • exclude (str | tuple[str, ...], default: () ) –

    Exclude tables and views matching the specified pattern(s)

Source code in src/db_diagram/_mermaid.py
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
def write_mermaid_markdown(
    metadata_source: (
        MetaData | Iterable[Table] | Table | str | URL | Engine | Connection
    ),
    directory: Path | str = "database",
    depth: int = 1,
    include: str | tuple[str, ...] = (),
    exclude: str | tuple[str, ...] = (),
) -> None:
    """
    Write mermaid markdown documents for the specified
    base class in the specified directory.

    Parameters:
        metadata_source: A SQLAlchemy database connection, connection string,
            metadata instance, or tables
        directory: The directory under which to which to write the
            mermaid markdown documents.
        depth: The depth of the relationship graph to include in each
            diagram
        include: Include only tables and views matching the
            specified pattern(s)
        exclude: Exclude tables and views matching the specified pattern(s)
    """
    if isinstance(metadata_source, (str, URL, Engine, Connection)):
        metadata_source = tuple(
            get_bind_metadata(metadata_source).tables.values()
        )
    if isinstance(metadata_source, MetaData):
        metadata_source = metadata_source.tables.values()
    elif isinstance(metadata_source, Table):
        metadata_source = (metadata_source,)
    if isinstance(directory, str):
        directory = Path(directory)
    if not directory.exists():
        directory.mkdir(parents=True, exist_ok=True)
    for table_name, mermaid_diagram in iter_tables_mermaid_diagrams(
        metadata_source,
        depth=depth,
        include=include,
        exclude=exclude,
    ):
        path: Path = directory / f"{table_name}.mmd"
        with path.open("w", newline="\n") as path_io:
            path_io.write(mermaid_diagram)