sanab3343
sanab3343

Reputation: 174

Write a m-to-m relation in sea-orm migration

I'm trying to create a many-to-many relationship in a SeaORM migration, but I'm facing an issue where the generated Related trait doesn't include the via method despite my attempts.

Context:

Code Snippet:

migration

// We probebly don't even need this table, but Im adding it just for making calls to telegram fewer.

use sea_orm_migration::prelude::*;

use crate::{m20240303_000003_create_caller::Caller, m20240303_000006_create_profile::Profile};

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Group::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(Group::Id)
                            .big_integer()
                            .not_null()
                            .primary_key()
                            .unique_key(),
                    )
                    .col(ColumnDef::new(Group::ActiveProfile).integer().null())
                    .col(ColumnDef::new(Group::IsMixed).boolean().not_null())
                    .foreign_key(
                        ForeignKey::create()
                            .from(Group::Table, Group::ActiveProfile)
                            .to(Profile::Table, Profile::Id)
                            .on_delete(ForeignKeyAction::SetNull)
                            .on_update(ForeignKeyAction::Cascade),
                    )
                    .to_owned(),
            )
            .await?;

        manager
            .create_table(
                Table::create()
                    .table(GroupCaller::Table)
                    .if_not_exists()
                    .primary_key(
                        Index::create()
                            .col(GroupCaller::GroupId)
                            .col(GroupCaller::CallerId),
                    )
                    .col(
                        ColumnDef::new(GroupCaller::GroupId)
                            .big_integer()
                            .not_null(),
                    )
                    .col(
                        ColumnDef::new(GroupCaller::CallerId)
                            .big_integer()
                            .not_null(),
                    )
                    .foreign_key(
                        ForeignKey::create()
                            .from(GroupCaller::Table, GroupCaller::GroupId)
                            .to(Group::Table, Group::Id)
                            .on_delete(ForeignKeyAction::Cascade)
                            .on_update(ForeignKeyAction::Cascade),
                    )
                    .foreign_key(
                        ForeignKey::create()
                            .from(GroupCaller::Table, GroupCaller::CallerId)
                            .to(Caller::Table, Caller::UserId)
                            .on_update(ForeignKeyAction::Cascade)
                            .on_delete(ForeignKeyAction::Cascade),
                    )
                    .to_owned(),
            )
            .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .drop_table(
                Table::drop()
                    .table(Group::Table)
                    .table(GroupCaller::Table)
                    .to_owned(),
            )
            .await
    }
}

#[derive(DeriveIden)]
pub enum Group {
    Table,
    /// Id of the group
    Id,
    /// Id of active profile if any
    ActiveProfile,
    /// Is the profile mixed? happen when safe
    /// changing between profiles.
    IsMixed,
}

#[derive(DeriveIden)]
pub enum GroupCaller {
    Table,
    GroupId,
    CallerId,
}

generated code

group.rs

//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.14

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "group")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub id: i64,
    pub active_profile: Option<i32>,
    pub is_mixed: bool,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::group_caller::Entity")]
    GroupCaller,
    #[sea_orm(
        belongs_to = "super::profile::Entity",
        from = "Column::ActiveProfile",
        to = "super::profile::Column::Id",
        on_update = "Cascade",
        on_delete = "SetNull"
    )]
    Profile,
}

impl Related<super::group_caller::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::GroupCaller.def()
    }
}

impl Related<super::profile::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Profile.def()
    }
}

impl Related<super::caller::Entity> for Entity {
    fn to() -> RelationDef {
        super::group_caller::Relation::Caller.def()
    }
    fn via() -> Option<RelationDef> {
        Some(super::group_caller::Relation::Group.def().rev())
    }
}

impl ActiveModelBehavior for ActiveModel {}

group_caller.rs

//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.14

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "group_caller")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub group_id: i64,
    #[sea_orm(primary_key, auto_increment = false)]
    pub caller_id: i64,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(
        belongs_to = "super::caller::Entity",
        from = "Column::CallerId",
        to = "super::caller::Column::UserId",
        on_update = "Cascade",
        on_delete = "Cascade"
    )]
    Caller,
    #[sea_orm(
        belongs_to = "super::group::Entity",
        from = "Column::GroupId",
        to = "super::group::Column::Id",
        on_update = "Cascade",
        on_delete = "Cascade"
    )]
    Group,
}

impl Related<super::caller::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Caller.def()
    }
}

impl Related<super::group::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Group.def()
    }
}

impl ActiveModelBehavior for ActiveModel {}

Question:

Am I missing something in my approach? What steps are necessary to establish a many-to-many relationship within a SeaORM migration, including generating the via method in the Related trait?

Additional Information:

Upvotes: 2

Views: 941

Answers (1)

frankie
frankie

Reputation: 111

You’re doing right except one thing. The junction table should respect following requirements, it should have:

  1. Exactly 2 foreign keys
  2. A primary key formed by exactly 2 columns
  3. Number of primary key fields equals to number of column

Reference

For example you should add the primary key to the junction table like this:

.primary_key(
    Index::create()
    .table(UserRole::Table)
    .col(UserRole::UserId)
    .col(UserRole::RoleId),
 )

Upvotes: 1

Related Questions