Complex Relations (M-N, Self-Referential)
Relevant source files
The following files were used as context for generating this wiki page:
SeaORM 2.0 provides a high-level API for modeling complex database relationships, specifically Many-to-Many (M-N) and Self-Referential associations. These relations are defined directly within the Model struct using the #[sea_orm::model] attribute, replacing the legacy pattern of manual trait implementations. This approach allows developers to traverse complex links, such as social media followers or tagged content, through conceptual declarations rather than manual join logic.
Sources: CLAUDE.md:12-23, sea-orm-sync/src/lib.rs:43-46
Many-to-Many (M-N) Relationships
Many-to-Many relationships in SeaORM are implemented using a junction table (also known as a link or associative table). The relationship is declared using the via parameter in the #[sea_orm(has_many)] attribute. This configuration allows an entity to relate to multiple instances of another entity (and vice versa) by referencing the intermediary table that stores the composite primary keys of both related entities.
Sources: CLAUDE.md:43-45, examples/quickstart/src/main.rs:69-73
Junction Table Definition
A junction table typically contains two primary keys that act as foreign keys to the participating entities. In SeaORM 2.0, these are defined with auto_increment = false and use belongs_to attributes to map back to the source entities.
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "post_tag")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub post_id: i32,
#[sea_orm(primary_key, auto_increment = false)]
pub tag_id: i32,
#[sea_orm(belongs_to, from = "post_id", to = "id")]
pub post: Option<super::post::Entity>,
#[sea_orm(belongs_to, from = "tag_id", to = "id")]
pub tag: Option<super::tag::Entity>,
}
Sources: CLAUDE.md:54-67, examples/quickstart/src/main.rs:114-128
M-N Data Flow
The following diagram illustrates how SeaORM resolves a Many-to-Many relationship between a Post and a Tag through the PostTag junction table.
The Entity Loader handles these relations by performing two joins or utilizing a data loader to consolidate rows by the left entity, effectively eliminating the N+1 query problem.
Sources: sea-orm-sync/src/lib.rs:142-154, sea-orm-codegen/src/entity/writer/mermaid.rs:116-135
Self-Referential Relationships
Self-referential relationships occur when an entity has a relationship with itself. Common use cases include parent-child hierarchies (e.g., categories) or social graphs (e.g., followers). SeaORM uses the self_ref attribute to define these associations.
Sources: CLAUDE.md:47-48, examples/quickstart/src/main.rs:16-19
Direct and Reverse Self-References
A self-reference can be defined in two directions:
- Forward Reference: Defining a relationship to another instance of the same entity (e.g., "Followers").
- Reverse Reference: Defining the reciprocal relationship (e.g., "Following").
#[sea_orm(self_ref, via = "user_follower", from = "User", to = "Follower")]
pub followers: HasMany<Entity>,
#[sea_orm(self_ref, via = "user_follower", reverse)]
pub following: HasMany<Entity>,
Sources: examples/quickstart/src/main.rs:16-19, sea-orm-codegen/src/entity/writer/dense.rs:159-183
Self-Referential Logic Flow
The following sequence diagram shows the logic of adding a follower in a self-referential model.
Implementation Attributes Summary
| Attribute | Parameter | Description |
|---|---|---|
via | table_name | Specifies the junction table for M-N or self-ref relations. |
from | column_name | Specifies the source column in the relationship mapping. |
to | column_name | Specifies the destination column in the target entity. |
self_ref | N/A | Flags the relationship as self-referential. |
reverse | N/A | Used in self-referencing to define the reciprocal relation. |
relation_enum | EnumName | Names the relation for use in complex query building. |
Sources: CLAUDE.md:37-48, sea-orm-codegen/src/entity/writer/dense.rs:135-157
Transformer Logic and Code Generation
The EntityTransformer and EntityWriter modules automate the detection and generation of these complex relations. When the codegen tool identifies a table with exactly two relations and a composite primary key of size two, it treats it as a ConjunctRelation (Many-to-Many).
Relation Type Detection
The transformer determines the relation type based on uniqueness constraints:
- If the columns involved in a relation are unique (or are the primary key), it is typed as
HasOne. - Otherwise, it is typed as
HasMany. - Junction tables are identified to generate the appropriate
viaattributes in the participating entities.
Sources: sea-orm-codegen/src/entity/transformer.rs:149-178, sea-orm-codegen/src/entity/transformer.rs:223-241
Code Generation for Dense Format
The gen_dense_model_struct function iterates through identified relations to produce the Rust fields. It pluralizes field names for HasMany relations and correctly applies self_ref logic for entities that reference their own table_name.
Sources: sea-orm-codegen/src/entity/writer/dense.rs:130-185
Conclusion
Complex relations in SeaORM 2.0 prioritize developer ergonomics by shifting implementation details into high-level attributes. By defining M-N relations via junction tables and utilizing the self_ref attribute for hierarchies, the ORM handles the underlying transaction management, join operations, and data loading requirements automatically.
Sources: sea-orm-sync/src/lib.rs:43-46, examples/quickstart/src/main.rs:142-146