Entity relationship diagrams for Python data model classes like Pydantic.
—
Erdantic provides high-level convenience functions for quick diagram generation without needing to manage EntityRelationshipDiagram instances directly. These functions automatically create diagrams, analyze models, and produce output in a single step.
def create(*models_or_modules: Union[type, ModuleType], terminal_models: Collection[type] = tuple(),
termini: Collection[type] = tuple(), limit_search_models_to: Optional[Collection[str]] = None) -> EntityRelationshipDiagram:
"""Construct [`EntityRelationshipDiagram`][erdantic.core.EntityRelationshipDiagram] from given
data model classes or modules.
Args:
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
to search for data model classes.
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
will stop searching for component classes when it reaches these models
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
limit_search_models_to (Collection[str] | None): Plugin identifiers to limit to when
searching modules for data model classes. Defaults to None which will not impose any
limits.
Returns:
EntityRelationshipDiagram: diagram object for given data model.
Raises:
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
automatically resolved.
"""
def draw(*models_or_modules: Union[type, ModuleType], out: Union[str, os.PathLike],
terminal_models: Collection[type] = tuple(), termini: Collection[type] = tuple(),
limit_search_models_to: Optional[Collection[str]] = None,
graph_attr: Optional[Mapping[str, Any]] = None, node_attr: Optional[Mapping[str, Any]] = None,
edge_attr: Optional[Mapping[str, Any]] = None, **kwargs):
"""Render entity relationship diagram for given data model classes to file.
Args:
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
to search for data model classes.
out (str | os.PathLike): Output file path for rendered diagram.
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
will stop searching for component classes when it reaches these models
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
limit_search_models_to (Optional[Collection[str]]): Plugin identifiers to limit to when
searching modules for data model classes. Defaults to None which will not impose any
limits.
graph_attr (Mapping[str, Any] | None, optional): Override any graph attributes on
the `pygraphviz.AGraph` instance. Defaults to None.
node_attr (Mapping[str, Any] | None, optional): Override any node attributes for all
nodes on the `pygraphviz.AGraph` instance. Defaults to None.
edge_attr (Mapping[str, Any] | None, optional): Override any edge attributes for all
edges on the `pygraphviz.AGraph` instance. Defaults to None.
**kwargs: Additional keyword arguments to
[`pygraphviz.AGraph.draw`][pygraphviz.AGraph.draw].
Raises:
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
automatically resolved.
"""
def to_dot(*models_or_modules: Union[type, ModuleType], terminal_models: Collection[type] = [],
termini: Collection[type] = tuple(), limit_search_models_to: Optional[Collection[str]] = None,
graph_attr: Optional[Mapping[str, Any]] = None, node_attr: Optional[Mapping[str, Any]] = None,
edge_attr: Optional[Mapping[str, Any]] = None) -> str:
"""Generate Graphviz [DOT language](https://graphviz.org/doc/info/lang.html) representation of
entity relationship diagram for given data model classes.
Args:
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
to search for data model classes.
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
will stop searching for component classes when it reaches these models
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
limit_search_models_to (Optional[Collection[str]]): Plugin identifiers to limit to when
searching modules for data model classes. Defaults to None which will not impose any
limits.
graph_attr (Mapping[str, Any] | None, optional): Override any graph attributes on
the `pygraphviz.AGraph` instance. Defaults to None.
node_attr (Mapping[str, Any] | None, optional): Override any node attributes for all
nodes on the `pygraphviz.AGraph` instance. Defaults to None.
edge_attr (Mapping[str, Any] | None, optional): Override any edge attributes for all
edges on the `pygraphviz.AGraph` instance. Defaults to None.
Returns:
str: DOT language representation of diagram
"""
def find_models(module: ModuleType, limit_search_models_to: Optional[Collection[str]] = None) -> Iterator[type]:
"""Searches a module and yields all data model classes found.
Args:
module (ModuleType): Module to search for data model classes.
limit_search_models_to (Collection[str] | None): Plugin identifiers to limit to when
searching modules for data model classes. Defaults to None which will not impose any
limits.
Yields:
Iterator[type]: Members of module that are data model classes.
Raises:
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
automatically resolved.
"""from erdantic import create, draw, to_dot, find_models, list_plugins
from types import ModuleType
from typing import Union, Collection, Optional, Mapping, Any, Iterator
import osfrom pydantic import BaseModel
from erdantic import draw
class User(BaseModel):
name: str
email: str
class Post(BaseModel):
title: str
author: User
# Generate diagram directly from model classes
draw(User, Post, out="quick_diagram.png")from erdantic import create
# Create a diagram object for further manipulation
diagram = create(User, Post)
# Now you can use diagram methods
diagram.draw("output.svg")
dot_content = diagram.to_dot()import my_models_module
from erdantic import draw
# Analyze entire module for data model classes
draw(my_models_module, out="module_diagram.png")
# Limit search to specific plugin types
draw(my_models_module, out="pydantic_only.png", limit_search_models_to=["pydantic"])from erdantic import draw
class BaseEntity(BaseModel):
id: int
created_at: datetime
class User(BaseEntity):
name: str
email: str
class Post(BaseEntity):
title: str
author: User
# Stop analysis at BaseEntity - don't recurse into its fields
draw(User, Post, terminal_models=[BaseEntity], out="with_terminal.png")from erdantic import draw
# Custom graph appearance
graph_style = {
"rankdir": "TB", # Top-to-bottom layout
"bgcolor": "lightgray",
"label": "My Application Models"
}
node_style = {
"fillcolor": "lightblue",
"style": "filled,rounded",
"fontname": "Arial"
}
edge_style = {
"color": "darkblue",
"penwidth": 2
}
draw(
User, Post,
out="styled_diagram.svg",
graph_attr=graph_style,
node_attr=node_style,
edge_attr=edge_style
)from erdantic import to_dot
# Generate DOT language representation
dot_string = to_dot(User, Post)
print(dot_string)
# Save for external processing
with open("models.dot", "w") as f:
f.write(dot_string)
# Generate with custom attributes
custom_dot = to_dot(
User, Post,
graph_attr={"rankdir": "TD"},
node_attr={"shape": "box"}
)from erdantic import find_models
import my_models_module
# Find all supported models in a module
models = list(find_models(my_models_module))
print(f"Found {len(models)} model classes: {[m.__name__ for m in models]}")
# Find only specific model types
pydantic_models = list(find_models(my_models_module, limit_search_models_to=["pydantic"]))
dataclass_models = list(find_models(my_models_module, limit_search_models_to=["dataclasses"]))
# Use discovered models directly
from erdantic import draw
draw(*models, out="all_discovered_models.png")from erdantic import draw, list_plugins
# See available plugins
print("Available plugins:", list_plugins())
# Generate diagrams for specific model types only
draw(my_module, out="pydantic_models.png", limit_search_models_to=["pydantic"])
draw(my_module, out="dataclass_models.png", limit_search_models_to=["dataclasses"])
draw(my_module, out="attrs_models.png", limit_search_models_to=["attrs"])from erdantic import draw
from erdantic.exceptions import UnknownModelTypeError, UnresolvableForwardRefError
try:
draw(MyModel, out="diagram.png")
except UnknownModelTypeError as e:
print(f"Model type not supported: {e.model}")
print(f"Available plugins: {e.available_plugins}")
except UnresolvableForwardRefError as e:
print(f"Cannot resolve forward reference '{e.name}' in model {e.model_full_name}")
except FileNotFoundError as e:
print(f"Output directory does not exist: {e}")# One-liner diagram generation
draw(User, Post, out="simple.png")# Multi-step with more control options
diagram = EntityRelationshipDiagram()
diagram.add_model(User)
diagram.add_model(Post)
diagram.draw("controlled.png")
# Access to diagram data
print(f"Models analyzed: {len(diagram.models)}")
print(f"Relationships found: {len(diagram.edges)}")Install with Tessl CLI
npx tessl i tessl/pypi-erdantic