如何在Prisma多对多关系中插入引用未存在记录的外键?
我需要建模一个多对多关系,但关联的其中一侧条目可能暂未存在。我最初用了如下Schema,但无法正常工作:
model Foo { id Int @id } model Bar { id Int @id } model FooBar { fooId Int barId Int foo Foo @relation(fields: [fooId], references: [id]) bar Bar? @relation(fields: [barId], references: [id]) @@id([fooId, barId]) }
当尝试插入已存在的fooId: 1和暂未存在的barId: 1的关联时,触发了以下错误:
Type: undefined
Message:
Invalidprisma.fooBar.create()invocation:An operation failed because it depends on one or more records that were required but not found. No 'Bar' record(s) (needed to inline the relation on 'fooBar' record(s)) was found for a nested connect on one-to-many relation 'fooBarToBar'.
Code: P2025
我需要一个可行方案:既能插入这种关联,又能在执行Foo查询{ include: { fooBar: { include: { bar: true }}}}时,在Bar不存在的情况下返回如下结构:
{ "id": 1, "fooBar": { "fooId": 1, "barId": 1, "bar": null } }
我试过上面的Schema但无效,猜测改成以下Schema能解决问题,但会产生冗余数据:
model FooBar { fooId Int barId Int barIdExisting Int? foo Foo @relation(fields: [fooId], references: [id]) bar Bar? @relation(fields: [barIdExisting], references: [id]) }
也就是仅当Bar存在时,将barIdExisting设为barId,但这样会造成数据冗余。
无冗余的正确解决方案
不需要冗余字段,只需把存储待关联Bar ID的普通字段和关联Bar表的外键字段拆分即可:
model Foo { id Int @id fooBar FooBar[] } model Bar { id Int @id fooBar FooBar[] } model FooBar { fooId Int desiredBarId Int // 存储目标Bar的ID,无论该Bar是否存在 barId Int? // 可选外键,仅当Bar存在时赋值 foo Foo @relation(fields: [fooId], references: [id]) bar Bar? @relation(fields: [barId], references: [id]) @@id([fooId, desiredBarId]) // 用fooId和desiredBarId作为复合主键 }
操作说明:
- 插入关联:如果Bar不存在,只赋值
fooId和desiredBarId,barId留空;如果Bar存在,可同时赋值fooId、desiredBarId和barId,或者通过connect关联对应的Bar记录。 - 查询结果:执行你需要的查询语句后,当Bar不存在时,
bar字段会返回null,同时desiredBarId保留你存储的目标ID,返回结构示例:
{ "id": 1, "fooBar": [ { "fooId": 1, "desiredBarId": 1, "barId": null, "bar": null } ] }
原错误原因:
你最初的Schema把barId同时用作普通字段和外键,Prisma会强制外键关联的记录必须存在,因此当Bar不存在时无法插入。拆分字段后,desiredBarId仅存储ID值不做约束,barId作为可选外键仅在Bar存在时生效,既满足了存储未存在Bar的ID的需求,又避免了数据冗余。
内容的提问来源于stack exchange,提问作者darthmaim

