Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

has_one relation is not recognized with primary foreign key (class table inheritance) #1884

Closed
Expurple opened this issue Sep 28, 2023 · 6 comments · Fixed by #2254 · May be fixed by #2236
Closed

has_one relation is not recognized with primary foreign key (class table inheritance) #1884

Expurple opened this issue Sep 28, 2023 · 6 comments · Fixed by #2254 · May be fixed by #2236
Assignees

Comments

@Expurple
Copy link
Contributor

Expurple commented Sep 28, 2023

Description

When I use class table inheritance, sea-orm generates a has_many relation from Parent to Child instead of has_one. This issue is somewhat similar to #1393, but my foreign key is also a primary key and this issue happens with both PostgreSQL and SQLite. PostgreSQL is used by the actual codebase where I first encountered this, and I chose SQLite for the reproducer to make it more simple and portable.

Steps to Reproduce

  1. Set up a dummy sea-orm project with migrations and with SQLite driver enabled.
  2. Paste the migration code from the Reproducible Example section.
  3. Generate entities:
    touch db.sqlite3
    DATABASE_URL="sqlite://./db.sqlite3" sea-orm-cli migrate fresh
    sea-orm-cli generate entity -u "sqlite://./db.sqlite3" -o src/entities
  4. Open src/entities/parent.rs

Expected Behavior

I expected to see has_one instead of has_many.

Actual Behavior

// In generated src/entities/parent.rs

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::child::Entity")]
    Child,
}

Reproduces How Often

Always.

Workarounds

In the actual codebase with PostgreSQL and different migration code, if I turn the Child's .primary_key() into a .unique_key(), has_one is generated as expected. However, the Child entity doesn't compile afterwards because it must have a primary key. In the reproducer migration with SQLite this doesn't happen (it still generates has_many). I didn't investigate the reason yet. Ping me if this is important.

Reproducible Example

use sea_orm_migration::prelude::*;

#[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(Parent::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(Parent::Id)
                            .integer()
                            .not_null()
                            .auto_increment()
                            .primary_key(),
                    )
                    .to_owned(),
            )
            .await?;
        manager
            .create_table(
                Table::create()
                    .table(Child::Table)
                    .if_not_exists()
                    .col(ColumnDef::new(Child::Id).integer().not_null().primary_key())
                    .foreign_key(
                        ForeignKey::create()
                            .from(Child::Table, Child::Id)
                            .to(Parent::Table, Parent::Id),
                    )
                    .to_owned(),
            )
            .await
    }

    async fn down(&self, _: &SchemaManager) -> Result<(), DbErr> {
        unimplemented!();
    }
}

#[derive(DeriveIden)]
pub enum Parent {
    Table,
    Id,
}

#[derive(DeriveIden)]
pub enum Child {
    Table,
    Id,
}

Versions

Linux 6.2.0-33-generic
PostgreSQL 16
SQLite 3.40.1

cargo tree | grep sea- in the actual project with PostrgeSQL:

├── sea-orm-migration v0.12.2
│   ├── sea-orm v0.12.2
│   │   ├── sea-orm-macros v0.12.2 (proc-macro)
│   │   │   ├── sea-bae v0.2.0 (proc-macro)
│   │   ├── sea-query v0.30.0
│   │   │   ├── sea-query-derive v0.4.0 (proc-macro)
│   │   ├── sea-query-binder v0.5.0
│   │   │   ├── sea-query v0.30.0 (*)
│   ├── sea-orm-cli v0.12.2
│   │   ├── sea-schema v0.14.0
│   │   │   ├── sea-query v0.30.0 (*)
│   │   │   └── sea-schema-derive v0.2.0 (proc-macro)
│   ├── sea-schema v0.14.0 (*)
├── sea-orm v0.12.2 (*)
├── sea-query v0.30.0 (*)
@Scooter1337
Copy link

Scooter1337 commented Oct 5, 2023

My Prisma ORM schema has as 1-1 relation that sea-orm doesn't recognise too.

A simplified version of my schema:

-- CreateTable
CREATE TABLE "Cart" (
    "id" SERIAL NOT NULL,
    "userId" INTEGER NOT NULL,
    CONSTRAINT "Cart_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "User" (
    "id" SERIAL NOT NULL,
    CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Cart_userId_key" ON "Cart"("userId");

-- AddForeignKey
ALTER TABLE "Cart"
ADD CONSTRAINT "Cart_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

Results in:

pub enum Relation {
    #[sea_orm(has_many = "super::cart::Entity")]
    Cart,
}

While the index clearly restricts the user from having multiple carts.

@SZenglein
Copy link
Contributor

The "solution" to this problem in my case is to add unique to my primary key column that references the parent.
In the new migration format:

.col(integer_uniq(Child::Id).primary_key())

This generates the following SQLite schema:

"id" integer NOT NULL UNIQUE PRIMARY KEY

IMO the issue is simply that a primary key column is not recognized as unique by sea-orm-cli. Adding a unique constraint to a primary key should not change anything.

@billy1624
Copy link
Member

Hey everyone, I just made a PR to fix this.

Install sea-orm-cli and try it on your local machine:

cargo install sea-orm-cli --force --git https://github.com/SeaQL/sea-orm --branch cli-gen-has-one-relation

@billy1624 billy1624 self-assigned this Jun 7, 2024
@Expurple
Copy link
Contributor Author

Hi. The issue still reproduces with the setup from my description. Here's an archive just in case:

seaorm-repro-1884.zip

Extract it, go to the root and then proceed to steps 3 and 4.

@billy1624
Copy link
Member

Hey @Expurple, thanks for pointing that out! This should fix it 4bf80ce

//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0-rc.5

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

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "parent")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_one = "super::child::Entity")]
    Child,
}

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

impl ActiveModelBehavior for ActiveModel {}

@Expurple
Copy link
Contributor Author

Expurple commented Jun 13, 2024

Yes, my case is fixed now. Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants