`has_one` relation is not recognized with primary foreign key (class table inheritance)
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
- Set up a dummy sea-orm project with migrations and with SQLite driver enabled.
- Paste the migration code from the Reproducible Example section.
- 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 - 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 (*)
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.
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.
Hey everyone, I just made a PR to fix this.
- https://github.com/SeaQL/sea-orm/pull/2254
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
Hi. The issue still reproduces with the setup from my description. Here's an archive just in case:
Extract it, go to the root and then proceed to steps 3 and 4.
Hey @Expurple, thanks for pointing that out! This should fix it https://github.com/SeaQL/sea-orm/pull/2254/commits/4bf80ce27b24be17d034870c0d8066030405693d
//! `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 {}
Yes, my case is fixed now. Thank you