Relationships¶
Relationships are declared with annotations: Annotated[Other, ForeignKey(...)] for the owning side, BackRef() for the reverse side, and ManyToMany(...) for join-table relations. At runtime, related data is accessed through Relation — an awaitable, chainable query bound to the instance. See the Relationships guide for usage patterns.
ForeignKey
¶
Describe a forward foreign-key relationship between models
Attributes:
to: Target model class resolved during model binding.
related_name: Name of the reverse relationship attribute on the target model.
on_delete: Referential action applied when the parent row is deleted.
unique: Treat the relation as one-to-one when True.
index: Request a non-unique index on the shadow ``*_id`` column.
nullable: Alembic nullability for the shadow ``*_id`` column (see
:class:`FerroField` ``nullable``). When ``'infer'``, uses whether the
**relation** annotation allows ``None``.
Examples:
>>> from typing import Annotated
>>> from ferro.models import Model
>>>
>>> class User(Model):
... id: Annotated[int, FerroField(primary_key=True)]
>>>
>>> class Post(Model):
... id: Annotated[int, FerroField(primary_key=True)]
... author: Annotated[int, ForeignKey("posts", on_delete="CASCADE")]
Source code in src/ferro/base.py
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
Attributes¶
to = None
instance-attribute
¶
related_name = related_name
instance-attribute
¶
on_delete = on_delete
instance-attribute
¶
unique = unique
instance-attribute
¶
index = index
instance-attribute
¶
nullable = _validate_nullable_option(nullable, 'ForeignKey')
instance-attribute
¶
relation_annotation = None
instance-attribute
¶
Functions¶
__init__(related_name, on_delete='CASCADE', unique=False, index=False, nullable='infer')
¶
Initialize foreign-key relationship metadata
Args:
related_name: Name for reverse access from the related model.
on_delete: Referential action for parent deletion.
Common values include "CASCADE", "RESTRICT", "SET NULL", "SET DEFAULT", and "NO ACTION".
unique: Set to True to enforce one-to-one behavior. Implies an
index, so combining ``unique=True`` with ``index=True`` is
redundant; ``index=True`` will be ignored and a ``UserWarning``
will be raised.
index: Set to True to create a non-unique index on the shadow
``*_id`` column. Useful for tenant FKs queried on every list
endpoint where Postgres does not auto-index the FK column.
nullable: See :class:`ForeignKey` class docstring.
Examples:
>>> from typing import Annotated
>>> from ferro import BackRef, ForeignKey, Relation
>>> from ferro.models import Model
>>>
>>> class Org(Model):
... id: Annotated[int, FerroField(primary_key=True)]
... projects: Relation[list["Project"]] = BackRef()
>>>
>>> class Project(Model):
... id: Annotated[int, FerroField(primary_key=True)]
... org: Annotated[Org, ForeignKey("projects", index=True)]
Source code in src/ferro/base.py
BackRef
¶
Declare a reverse relationship field.
BackRef() is a convenience wrapper around Field(back_ref=True).
Source code in src/ferro/fields.py
ManyToMany(*, related_name, through=None, reverse_index=True, **kwargs)
¶
Declare a many-to-many relationship field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
related_name
|
str
|
Name for reverse access from the related model. |
required |
through
|
str | None
|
Optional explicit join table name. When omitted, Ferro generates a join table name automatically. |
None
|
reverse_index
|
bool
|
When True (default), the synthesized join table gets
a non-unique composite index on |
True
|
Source code in src/ferro/fields.py
Relation
¶
Bases: Query[T]
Represent lazy collection relationship queries with typing support
Examples:
>>> class User(Model):
... id: Annotated[int, FerroField(primary_key=True)]
... name: str
... posts: Relation[list["Post"]] = BackRef()
>>> class Post(Model):
... id: Annotated[int, FerroField(primary_key=True)]
... title: str
... user: Annotated[User, ForeignKey(related_name="posts")]