Renders an EntityRelationshipDiagram into the D2 class diagram format.
Source code in erdantic/d2.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 | def render_d2(diagram: EntityRelationshipDiagram) -> str:
"""Renders an EntityRelationshipDiagram into the D2 class diagram format."""
d2_parts: list[str] = []
# Define all class shapes first
for model in diagram.models.values():
class_name = _quote_identifier(model.name)
class_def = [f"{class_name}: {{", " shape: class"]
if not model.fields:
class_def.append(" # This class has no fields to display in the diagram.")
else:
for field in model.fields.values():
field_type = _maybe_quote_value(field.type_name)
visibility = _get_visibility_prefix(field.name)
class_def.append(f" {visibility}{field.name}: {field_type}")
class_def.append("}\n")
d2_parts.append("\n".join(class_def))
# Define all relationships between classes
for edge in diagram.edges.values():
source_model = diagram.models.get(str(edge.source_model_full_name))
target_model = diagram.models.get(str(edge.target_model_full_name))
if not source_model or not target_model:
continue
source_model_name = _quote_identifier(source_model.name)
target_model_name = _quote_identifier(target_model.name)
label = _quote_identifier(edge.source_field_name)
connection = "->" # Directed from source to target
target_shape = _get_crowsfoot_d2(edge.target_cardinality, edge.target_modality)
attributes = [f"target-arrowhead.shape: {target_shape}"]
# Source side: omit entirely when both are UNSPECIFIED. Otherwise, map and include.
if not (
edge.source_cardinality == Cardinality.UNSPECIFIED
and edge.source_modality == Modality.UNSPECIFIED
):
source_shape = _get_crowsfoot_d2(edge.source_cardinality, edge.source_modality)
attributes.append(f"source-arrowhead.shape: {source_shape}")
connection = "<->" # Bidirectional if source side is specified
d2_parts.append(
_REL_DEF_TEMPLATE.format(
source_model_name=source_model_name,
connection=connection,
target_model_name=target_model_name,
label=label,
attributes=indent("\n".join(attributes), " " * 2),
)
)
return "\n".join(d2_parts)
|