drawlib.diagrams.er
Relational database Entity-Relationship (ER) schema diagrams using Information Engineering (Crow's Foot) notation and row-accurate foreign key column anchoring. Canonical import: from drawlib.diagrams.er import Entity, ERDiagram, Relationship.
Source Code
from drawlib.canvas import save, setup
from drawlib.diagrams.er import Entity, ERDiagram
from drawlib.fonts import FontRoboto, FontSourceCode
from drawlib.styles import Colors, Styles
setup(width=120, height=58)
erd = ERDiagram(
node_style=Styles.Neutral.patch(text_font=FontSourceCode.ROBOTO_MONO, text_size=10.0),
header_style=Styles.PrimaryNeutral.patch(text_font=FontRoboto.ROBOTO_BOLD, text_size=11.0),
edge_style=Styles.DarkBold,
edge_text_style=Styles.Dark.patch(text_size=10.0),
)
users = erd.add(
Entity("users", width=42, header_height=4.8, row_height=4.0)
.add_column("id", "UUID", pk=True, nullable=False)
.add_column("email", "VARCHAR", nullable=False)
.add_column("created_at", "TIMESTAMP", nullable=False),
xy=(27.0, 43.0),
)
products = erd.add(
Entity(
"products",
width=42,
header_height=4.8,
row_height=4.0,
header_style=Styles.SecondaryNeutral.patch(text_font=FontRoboto.ROBOTO_BOLD, text_size=11.0),
)
.add_column("id", "UUID", pk=True, nullable=False)
.add_column("sku", "VARCHAR", nullable=False)
.add_column("price", "INT", nullable=False),
xy=(27.0, 15.0),
)
order_items = erd.add(
Entity(
"order_items",
width=44,
header_height=4.8,
row_height=4.0,
header_style=Styles.PrimaryFlat.patch(
text_color=Colors.White, text_font=FontRoboto.ROBOTO_BOLD, text_size=11.0
),
)
.add_column("id", "BIGINT", pk=True, nullable=False)
.add_column("user_id", "UUID", fk=True, nullable=False)
.add_column("product_id", "UUID", fk=True, nullable=False)
.add_column("qty", "INT", nullable=False),
xy=(92.0, 29.0),
)
users.connect(
order_items,
cardinality="1:*",
start_column="id",
end_column="user_id",
start_side="right",
end_side="left",
)
products.connect(
order_items,
cardinality="1:*",
start_column="id",
end_column="product_id",
start_side="right",
end_side="left",
)
erd.draw(xy=(0.0, 0.0))
save()
typeCardinality#
Literal union specifying Information Engineering (Crow's Foot) multiplicity markers at the start and end endpoints of a Relationship.
Cardinality = Literal["1:*", "1:1", "1:1..*", "1:0..1", "0..1:1", "0..1:*", "*:*"]
- Coordinate Anchor: Drawn immediately outside the resolved
startandendentity border or column-row anchor points.
| Literal Value | start Marker |
end Marker |
Relational Semantics |
|---|---|---|---|
"1:*" |
Mandatory One (||) |
Zero-or-Many (o< / |<) |
Standard 1-to-Many foreign key relationship (default). |
"1:1" |
Mandatory One (||) |
Mandatory One (||) |
Strict 1-to-1 relationship. |
"1:1..*" |
Mandatory One (||) |
Mandatory One-or-Many (|<) |
1-to-Many requiring at least one child row. |
"1:0..1" |
Mandatory One (||) |
Optional Zero-or-One (o|) |
Mandatory source to optional target row. |
"0..1:1" |
Optional Zero-or-One (o|) |
Mandatory One (||) |
Optional source to mandatory target row. |
"0..1:*" |
Optional Zero-or-One (o|) |
Many (|<) |
Nullable foreign key 1-to-Many relationship. |
"*:*" |
Many (>|) |
Many (|<) |
Many-to-Many association. |
classColumnInfo#
Validated Pydantic data model representing a single database table column row inside an Entity. Typically created via entity.add_column(...) or passed to entity.add_columns([...]).
ColumnInfo(
*,
name: str,
type: str = "",
pk: bool = False,
fk: bool = False,
nullable: bool = True,
) -> None
- Coordinate Anchor: Rendered as a 3-slot aligned row (
PK/FKbadge |name|type (NN)) insideEntity, addressable bynameinstart_column/end_columnrelationship anchors.
Constructor Parameters & Fields#
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
(Required) | Database column identifier (keyword-only). |
type |
str |
"" |
SQL data type label (e.g., "UUID", "VARCHAR(255)", "BIGINT"). |
pk |
bool |
False |
Marks the column as a Primary Key (renders "PK" badge and bold name). |
fk |
bool |
False |
Marks the column as a Foreign Key (renders "FK" or "PK,FK" badge). |
nullable |
bool |
True |
When False, appends (NN) (NOT NULL) beside type. |
Public Methods & Properties#
| Member | Kind | Signature | Description |
|---|---|---|---|
model_copy |
method |
(self, *, update: Mapping[str, Any] | None = None, deep: bool = False) -> Self |
Create a shallow or deep copy with optional field updates (pydantic.BaseModel). |
model_dump |
method |
(self, *, mode: str = "python", ...) -> dict[str, Any] |
Serialize column metadata to a Python dictionary (pydantic.BaseModel). |
model_dump_json |
method |
(self, *, indent: int | None = None, ...) -> str |
Serialize column metadata to a JSON string (pydantic.BaseModel). |
model_validate |
classmethod |
(obj: Any, *, strict: bool | None = None, ...) -> Self |
Validate an arbitrary mapping or object into ColumnInfo. |
model_validate_json |
classmethod |
(json_data: str | bytes | bytearray, ...) -> Self |
Parse and validate JSON input into ColumnInfo. |
model_validate_strings |
classmethod |
(obj: Any, ...) -> Self |
Validate string-typed input dictionary into ColumnInfo. |
model_construct |
classmethod |
(_fields_set: set[str] | None = None, **values: Any) -> Self |
Construct an unvalidated instance directly from field values. |
model_json_schema |
classmethod |
(by_alias: bool = True, ...) -> dict[str, Any] |
Generate a JSON Schema dictionary for ColumnInfo. |
(Also inherits standard pydantic.BaseModel lifecycle/compatibility members: model_extra, model_fields_set, model_parametrized_name, model_post_init, model_rebuild, construct, copy, dict, from_orm, json, parse_file, parse_obj, parse_raw, schema, schema_json, update_forward_refs, and validate.)
classERDiagram#
Top-level container for Entity-Relationship (ER) database schema diagrams managing Entity table cards and Crow's Foot Relationship lines.
ERDiagram(
*,
node_style: Style,
edge_style: Style,
edge_text_style: Style,
title: str = "",
title_style: Style | None = None,
width: float | None = None,
height: float | None = None,
style: Style | None = None,
header_style: Style | None = None,
) -> None
- Coordinate Anchor:
draw(xy=(0.0, 0.0), align="bottom_left")places the diagram's local(0.0, 0.0)origin at canvas coordinatexy(or centersget_size()aroundxywhenalign="center").
Constructor Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
node_style |
Style |
(Required) | Base style for Entity table cards, borders, and column text (keyword-only). |
edge_style |
Style |
(Required) | Base stroke style for Relationship lines and Crow's Foot markers (keyword-only). |
edge_text_style |
Style |
(Required) | Base typography style for relationship labels (keyword-only). |
title |
str |
"" |
Optional diagram title rendered centered above the schema. |
title_style |
Style | None |
None |
Optional typography style override for title. |
width |
float | None |
None |
Optional fixed diagram width in local units. |
height |
float | None |
None |
Optional fixed diagram height in local units. |
style |
Style | None |
None |
Optional outer background/border style for the diagram container. |
header_style |
Style | None |
None |
Optional default style applied to top entity header banners unless overridden per Entity. |
Public Methods & Properties#
| Member | Kind | Signature | Description |
|---|---|---|---|
add |
method |
(self, entity: Entity, xy: tuple[float, float], show: bool | None = None) -> Entity |
Register an Entity table centered at local coordinate xy and return it. |
add_relationship |
method |
(self, relationship: Relationship) -> Relationship |
Register a pre-constructed Relationship instance with this diagram. |
connect |
method |
(self, start: Entity, end: Entity, cardinality: Cardinality = "1:*", start_side: Side = "auto", end_side: Side = "auto", start_column: str | None = None, end_column: str | None = None, label: str = "", style: Style | None = None, text_style: Style | None = None, routing: RoutingType = "orthogonal", padding: PaddingType = 0.0, show: bool = True) -> Relationship |
Create and register a Crow's Foot Relationship between start and end. |
draw |
method |
(self, xy: tuple[float, float] = (0.0, 0.0), *, align: Literal["bottom_left", "center"] = "bottom_left", scale: PosFloat = 1.0) -> None |
Render the complete ER diagram onto the active canvas anchored at xy. |
entities |
property |
-> list[Entity] |
Return the list of Entity tables registered in the diagram. |
get_size |
method |
(self) -> tuple[float, float] |
Return the overall (width, height) of the diagram in unscaled local units. |
relationships |
property |
-> list[Relationship] |
Return the list of Relationship edges registered in the diagram. |
classEntity#
Database table card displaying a header banner and 3-subcolumn schema rows (PK/FK badge, column name, and SQL type + nullability).
Entity(
name: str,
width: float = 25.0,
height: float | None = None,
header_height: float = 4.0,
row_height: float = 3.2,
style: Style | None = None,
header_style: Style | None = None,
show: bool = True,
) -> None
- Coordinate Anchor: Center
(cx, cy)when registered viaerd.add(entity, xy=(cx, cy)). When highlighting a focal table header withStyles.PrimaryFlat, passheader_style=Styles.PrimaryFlat.patch(text_color=Colors.White, text_size=11.0)so the table name renders in crisp white text.
Constructor Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
str |
(Required) | Table or entity name displayed in the top header banner. |
width |
float |
25.0 |
Table card width in local units (use 36.0–44.0 when showing SQL types with (NN)). |
height |
float | None |
None |
Optional fixed table height override (None auto-computes header_height + max(len(columns), 1) * row_height). |
header_height |
float |
4.0 |
Vertical height of the top table name banner. |
row_height |
float |
3.2 |
Vertical height per column row. |
style |
Style | None |
None |
Optional body fill, border, and column text style override (falls back to erd.node_style). |
header_style |
Style | None |
None |
Optional header banner fill/border/text style override (falls back to erd.header_style). |
show |
bool |
True |
Visibility flag (False hides this entity card and connected relationships). |
Public Methods & Properties#
| Member | Kind | Signature | Description |
|---|---|---|---|
add_column |
method |
(self, name: str, type: str = "", pk: bool = False, fk: bool = False, nullable: bool = True) -> Entity |
Append a ColumnInfo row to this entity and return self for method chaining. |
add_columns |
method |
(self, columns: Sequence[Sequence[Any] | ColumnInfo]) -> Entity |
Append multiple columns in batch from (name, type, ...) tuples or ColumnInfo instances and return self. |
center |
property |
-> tuple[float, float] |
Relative center coordinate (cx, cy) of the entity card. |
connect |
method |
(self, target: Entity, cardinality: Cardinality = "1:*", start_side: Side = "auto", end_side: Side = "auto", start_column: str | None = None, end_column: str | None = None, label: str = "", style: Style | None = None, text_style: Style | None = None, routing: RoutingType = "orthogonal", padding: PaddingType = 0.0, show: bool = True) -> Relationship |
Connect this registered entity to target and register the Relationship on the parent ERDiagram. |
effective_height |
property |
-> float |
Actual rendered height of the entity card in local units. |
get_anchor |
method |
(self, side: Side, column_name: str | None = None) -> tuple[float, float] |
Return boundary coordinate on side, optionally aligned to the exact row Y of column_name. |
get_bounds |
method |
(self) -> tuple[float, float, float, float] |
Return local bounding box (-width/2, -effective_height/2, width/2, effective_height/2) relative to (0, 0). |
xy |
property |
-> tuple[float, float] |
Alias returning the relative center coordinate (cx, cy) of the entity card. |
classRelationship#
Crow's Foot connector linking two Entity tables at either card midpoints or exact foreign-key column rows (start_column, end_column).
Relationship(
start: Entity,
end: Entity,
cardinality: Cardinality = "1:*",
start_side: Side = "auto",
end_side: Side = "auto",
start_column: str | None = None,
end_column: str | None = None,
label: str = "",
style: Style | None = None,
text_style: Style | None = None,
routing: RoutingType = "orthogonal",
padding: PaddingType = 0.0,
show: bool = True,
) -> None
- Coordinate Anchor: Attaches to
start.get_anchor(start_side, start_column)andend.get_anchor(end_side, end_column). Register viaerd.connect(...),entity.connect(...), orerd.add_relationship(rel).
Constructor Parameters & Attributes#
| Parameter | Type | Default | Description |
|---|---|---|---|
start |
Entity |
(Required) | Source database entity. |
end |
Entity |
(Required) | Target database entity. |
cardinality |
Cardinality |
"1:*" |
Crow's Foot multiplicity notation ("1:*", "1:1", "1:1..*", "1:0..1", "0..1:1", "0..1:*", "*:*"). |
start_side |
Side |
"auto" |
Attachment side on start ("left", "right", "top", "bottom", "auto"). |
end_side |
Side |
"auto" |
Attachment side on end ("left", "right", "top", "bottom", "auto"). |
start_column |
str | None |
None |
Optional column name in start to snap the departure anchor directly to that row's Y coordinate (on "left"/"right" sides). |
end_column |
str | None |
None |
Optional column name in end to snap the arrival anchor directly to that row's Y coordinate (on "left"/"right" sides). |
label |
str |
"" |
Optional relationship verb or constraint label along the connector. |
style |
Style | None |
None |
Optional line stroke style override (falls back to erd.edge_style). |
text_style |
Style | None |
None |
Optional label typography style override (falls back to erd.edge_text_style). |
routing |
RoutingType |
"orthogonal" |
Path routing strategy ("orthogonal" or "direct"). |
padding |
float | tuple[float, float] |
0.0 |
Clearance gap between entity borders and Crow's Foot endpoints. |
show |
bool |
True |
Whether to render this relationship. |
typeRoutingType#
Literal type controlling connector geometry for Relationship edges.
RoutingType = Literal["orthogonal", "direct"]
- Coordinate Anchor: Governs path segments between resolved
startandendentity/column anchors.
| Literal Value | Path Geometry |
|---|---|
"orthogonal" |
Right-angled horizontal and vertical Manhattan segments (default; recommended for start_column/end_column). |
"direct" |
Straight Euclidean segment connecting the resolved anchors. |
typeSide#
Literal type specifying the attachment side on an Entity card.
Side = Literal["left", "right", "top", "bottom", "auto"]
- Coordinate Anchor:
"left"and"right"support row-level Y snapping when paired withstart_column/end_column;"auto"selects the nearest facing side automatically.