0
# Convenience Functions
1
2
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.
3
4
## Core Functions
5
6
```python { .api }
7
def create(*models_or_modules: Union[type, ModuleType], terminal_models: Collection[type] = tuple(),
8
termini: Collection[type] = tuple(), limit_search_models_to: Optional[Collection[str]] = None) -> EntityRelationshipDiagram:
9
"""Construct [`EntityRelationshipDiagram`][erdantic.core.EntityRelationshipDiagram] from given
10
data model classes or modules.
11
12
Args:
13
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
14
to search for data model classes.
15
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
16
will stop searching for component classes when it reaches these models
17
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
18
limit_search_models_to (Collection[str] | None): Plugin identifiers to limit to when
19
searching modules for data model classes. Defaults to None which will not impose any
20
limits.
21
22
Returns:
23
EntityRelationshipDiagram: diagram object for given data model.
24
25
Raises:
26
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
27
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
28
automatically resolved.
29
"""
30
31
def draw(*models_or_modules: Union[type, ModuleType], out: Union[str, os.PathLike],
32
terminal_models: Collection[type] = tuple(), termini: Collection[type] = tuple(),
33
limit_search_models_to: Optional[Collection[str]] = None,
34
graph_attr: Optional[Mapping[str, Any]] = None, node_attr: Optional[Mapping[str, Any]] = None,
35
edge_attr: Optional[Mapping[str, Any]] = None, **kwargs):
36
"""Render entity relationship diagram for given data model classes to file.
37
38
Args:
39
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
40
to search for data model classes.
41
out (str | os.PathLike): Output file path for rendered diagram.
42
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
43
will stop searching for component classes when it reaches these models
44
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
45
limit_search_models_to (Optional[Collection[str]]): Plugin identifiers to limit to when
46
searching modules for data model classes. Defaults to None which will not impose any
47
limits.
48
graph_attr (Mapping[str, Any] | None, optional): Override any graph attributes on
49
the `pygraphviz.AGraph` instance. Defaults to None.
50
node_attr (Mapping[str, Any] | None, optional): Override any node attributes for all
51
nodes on the `pygraphviz.AGraph` instance. Defaults to None.
52
edge_attr (Mapping[str, Any] | None, optional): Override any edge attributes for all
53
edges on the `pygraphviz.AGraph` instance. Defaults to None.
54
**kwargs: Additional keyword arguments to
55
[`pygraphviz.AGraph.draw`][pygraphviz.AGraph.draw].
56
57
Raises:
58
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
59
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
60
automatically resolved.
61
"""
62
63
def to_dot(*models_or_modules: Union[type, ModuleType], terminal_models: Collection[type] = [],
64
termini: Collection[type] = tuple(), limit_search_models_to: Optional[Collection[str]] = None,
65
graph_attr: Optional[Mapping[str, Any]] = None, node_attr: Optional[Mapping[str, Any]] = None,
66
edge_attr: Optional[Mapping[str, Any]] = None) -> str:
67
"""Generate Graphviz [DOT language](https://graphviz.org/doc/info/lang.html) representation of
68
entity relationship diagram for given data model classes.
69
70
Args:
71
*models_or_modules (type | ModuleType): Data model classes to add to diagram, or modules
72
to search for data model classes.
73
terminal_models (Collection[type]): Data model classes to set as terminal nodes. erdantic
74
will stop searching for component classes when it reaches these models
75
termini (Collection[type]): Deprecated. Use `terminal_models` instead.
76
limit_search_models_to (Optional[Collection[str]]): Plugin identifiers to limit to when
77
searching modules for data model classes. Defaults to None which will not impose any
78
limits.
79
graph_attr (Mapping[str, Any] | None, optional): Override any graph attributes on
80
the `pygraphviz.AGraph` instance. Defaults to None.
81
node_attr (Mapping[str, Any] | None, optional): Override any node attributes for all
82
nodes on the `pygraphviz.AGraph` instance. Defaults to None.
83
edge_attr (Mapping[str, Any] | None, optional): Override any edge attributes for all
84
edges on the `pygraphviz.AGraph` instance. Defaults to None.
85
86
Returns:
87
str: DOT language representation of diagram
88
"""
89
90
def find_models(module: ModuleType, limit_search_models_to: Optional[Collection[str]] = None) -> Iterator[type]:
91
"""Searches a module and yields all data model classes found.
92
93
Args:
94
module (ModuleType): Module to search for data model classes.
95
limit_search_models_to (Collection[str] | None): Plugin identifiers to limit to when
96
searching modules for data model classes. Defaults to None which will not impose any
97
limits.
98
99
Yields:
100
Iterator[type]: Members of module that are data model classes.
101
102
Raises:
103
UnknownModelTypeError: if a given model does not match any model types from loaded plugins.
104
UnresolvableForwardRefError: if a model contains a forward reference that cannot be
105
automatically resolved.
106
"""
107
108
```
109
110
## Required Imports
111
112
```python
113
from erdantic import create, draw, to_dot, find_models, list_plugins
114
from types import ModuleType
115
from typing import Union, Collection, Optional, Mapping, Any, Iterator
116
import os
117
```
118
119
## Usage Examples
120
121
### Quick Diagram Generation
122
123
```python
124
from pydantic import BaseModel
125
from erdantic import draw
126
127
class User(BaseModel):
128
name: str
129
email: str
130
131
class Post(BaseModel):
132
title: str
133
author: User
134
135
# Generate diagram directly from model classes
136
draw(User, Post, out="quick_diagram.png")
137
```
138
139
### Creating Diagram Objects
140
141
```python
142
from erdantic import create
143
144
# Create a diagram object for further manipulation
145
diagram = create(User, Post)
146
147
# Now you can use diagram methods
148
diagram.draw("output.svg")
149
dot_content = diagram.to_dot()
150
```
151
152
### Working with Modules
153
154
```python
155
import my_models_module
156
from erdantic import draw
157
158
# Analyze entire module for data model classes
159
draw(my_models_module, out="module_diagram.png")
160
161
# Limit search to specific plugin types
162
draw(my_models_module, out="pydantic_only.png", limit_search_models_to=["pydantic"])
163
```
164
165
### Terminal Models
166
167
```python
168
from erdantic import draw
169
170
class BaseEntity(BaseModel):
171
id: int
172
created_at: datetime
173
174
class User(BaseEntity):
175
name: str
176
email: str
177
178
class Post(BaseEntity):
179
title: str
180
author: User
181
182
# Stop analysis at BaseEntity - don't recurse into its fields
183
draw(User, Post, terminal_models=[BaseEntity], out="with_terminal.png")
184
```
185
186
187
### Advanced Styling
188
189
```python
190
from erdantic import draw
191
192
# Custom graph appearance
193
graph_style = {
194
"rankdir": "TB", # Top-to-bottom layout
195
"bgcolor": "lightgray",
196
"label": "My Application Models"
197
}
198
199
node_style = {
200
"fillcolor": "lightblue",
201
"style": "filled,rounded",
202
"fontname": "Arial"
203
}
204
205
edge_style = {
206
"color": "darkblue",
207
"penwidth": 2
208
}
209
210
draw(
211
User, Post,
212
out="styled_diagram.svg",
213
graph_attr=graph_style,
214
node_attr=node_style,
215
edge_attr=edge_style
216
)
217
```
218
219
### DOT Generation
220
221
```python
222
from erdantic import to_dot
223
224
# Generate DOT language representation
225
dot_string = to_dot(User, Post)
226
print(dot_string)
227
228
# Save for external processing
229
with open("models.dot", "w") as f:
230
f.write(dot_string)
231
232
# Generate with custom attributes
233
custom_dot = to_dot(
234
User, Post,
235
graph_attr={"rankdir": "TD"},
236
node_attr={"shape": "box"}
237
)
238
```
239
240
### Module Model Discovery
241
242
```python
243
from erdantic import find_models
244
import my_models_module
245
246
# Find all supported models in a module
247
models = list(find_models(my_models_module))
248
print(f"Found {len(models)} model classes: {[m.__name__ for m in models]}")
249
250
# Find only specific model types
251
pydantic_models = list(find_models(my_models_module, limit_search_models_to=["pydantic"]))
252
dataclass_models = list(find_models(my_models_module, limit_search_models_to=["dataclasses"]))
253
254
# Use discovered models directly
255
from erdantic import draw
256
draw(*models, out="all_discovered_models.png")
257
```
258
259
### Plugin Limitation
260
261
```python
262
from erdantic import draw, list_plugins
263
264
# See available plugins
265
print("Available plugins:", list_plugins())
266
267
# Generate diagrams for specific model types only
268
draw(my_module, out="pydantic_models.png", limit_search_models_to=["pydantic"])
269
draw(my_module, out="dataclass_models.png", limit_search_models_to=["dataclasses"])
270
draw(my_module, out="attrs_models.png", limit_search_models_to=["attrs"])
271
```
272
273
### Error Handling
274
275
```python
276
from erdantic import draw
277
from erdantic.exceptions import UnknownModelTypeError, UnresolvableForwardRefError
278
279
try:
280
draw(MyModel, out="diagram.png")
281
except UnknownModelTypeError as e:
282
print(f"Model type not supported: {e.model}")
283
print(f"Available plugins: {e.available_plugins}")
284
except UnresolvableForwardRefError as e:
285
print(f"Cannot resolve forward reference '{e.name}' in model {e.model_full_name}")
286
except FileNotFoundError as e:
287
print(f"Output directory does not exist: {e}")
288
```
289
290
## Comparison with Direct Diagram Usage
291
292
### Convenience Functions (Recommended for Simple Cases)
293
```python
294
# One-liner diagram generation
295
draw(User, Post, out="simple.png")
296
```
297
298
### Direct Diagram Usage (More Control)
299
```python
300
# Multi-step with more control options
301
diagram = EntityRelationshipDiagram()
302
diagram.add_model(User)
303
diagram.add_model(Post)
304
diagram.draw("controlled.png")
305
306
# Access to diagram data
307
print(f"Models analyzed: {len(diagram.models)}")
308
print(f"Relationships found: {len(diagram.edges)}")
309
```