SeaQL / SeaQL/sea-orm

sea-orm-cli generation doesn't support multi-column foreign keys

Open
#855 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Area:schema
Dominant language
Rust
Stars
9.9k
Forks
734
Avg merge
6h 36m
Merged PRs (30d)
8

Description

Description

For single-column foreign keys, the generated parent and child have a relation to each other. The parent has a simple has_many and the child has a belong_to with the details of the foreign key columns and on update/delete policies.

For multi-column foreign keys though, it generates one belongs_to relation on the child per column and doesn't generate a has_many relation on the parent. Therefore, for example, using find_with_related doesn't compile.

Steps to Reproduce

#[derive(Iden)]
enum Block {
    Table,
    Qidx,
    Bidx,
}

#[derive(Iden)]
enum Rule {
    Table,
    Qidx,
    Bidx,
    Ridx,
}

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(Table::create()
                .table(Block::Table)
                .if_not_exists()
                .col(ColumnDef::new(Block::Qidx).big_unsigned().not_null())
                .col(ColumnDef::new(Block::Bidx).big_unsigned().not_null())
                .primary_key(Index::create()
                    .name("PK-block")
                    .col(Rule::Qidx).col(Rule::Bidx)
                    .primary())
                .to_owned())
            .await?;

        manager
            .create_table(Table::create()
                .table(Rule::Table)
                .if_not_exists()
                .col(ColumnDef::new(Rule::Qidx).big_unsigned().not_null())
                .col(ColumnDef::new(Rule::Bidx).big_unsigned().not_null())
                .col(ColumnDef::new(Rule::Ridx).big_unsigned().not_null())
                .primary_key(Index::create()
                    .name("PK-rule")
                    .col(Rule::Qidx).col(Rule::Bidx).col(Rule::Ridx)
                    .primary())
                .foreign_key(ForeignKey::create()
                    .name("FK-rule-block")
                    .from(Rule::Table, (Rule::Qidx, Rule::Bidx))
                    .to(Block::Table, (Block::Qidx, Block::Bidx))
                    .on_delete(ForeignKeyAction::Cascade)
                    .on_update(ForeignKeyAction::Restrict))
                .to_owned())
            .await?;
    }

It will generate both of these files.

//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

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

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "block")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub qidx: i32,
    #[sea_orm(primary_key, auto_increment = false)]
    pub bidx: i32,
}

impl ActiveModelBehavior for ActiveModel {}
//! SeaORM Entity. Generated by sea-orm-codegen 0.8.0

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

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "rule")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub qidx: i32,
    #[sea_orm(primary_key, auto_increment = false)]
    pub bidx: i32,
    #[sea_orm(primary_key, auto_increment = false)]
    pub ridx: i32,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(
        belongs_to = "super::block::Entity",
        from = "Column::Qidx",
        to = "super::block::Column::Qidx",
        on_update = "Restrict",
        on_delete = "Cascade"
    )]
    Block2,
    #[sea_orm(
        belongs_to = "super::block::Entity",
        from = "Column::Bidx",
        to = "super::block::Column::Bidx",
        on_update = "Restrict",
        on_delete = "Cascade"
    )]
    Block1,
}

impl ActiveModelBehavior for ActiveModel {}
Expected Behavior

It should generate a has_many relation on Block pointing to Rule. Simply adding this by hand doesn't trivially work, I'm unsure how the Related trait should be implemented for a double relation like Block1 and Block2, is the relation on the child even correct?

Even if the support for this in the generation is not implemented in the short term, I would really appreciate some guidance on how to define the Entity manually so that this works as intended.

Versions

sea-orm 0.8.0

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the sea-orm-cli generation path and compare the generated Block and Rule entity files from the reproduction. Investigate how multi-column foreign keys are represented in generated relations and how Related is implemented for single-column relations. Done means generation creates a usable parent has_many relation and find_with_related compiles for the multi-column key.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.