3.1 关系的两条腿
Prisma 里关系需要在两侧 model 都声明:一侧是关系标量字段 + 关系字段(持有外键),另一侧是反向关系字段(列表或对象,虚拟,不建列)。
- 关系字段(relation field)
- 类型为另一个 model 的字段,如
author User、posts Post[]。它是 Prisma 层面的虚拟字段,不一定对应真实列。 - 关系标量字段(relation scalar)
- 真正存外键值的标量列,如
authorId Int,被@relation(fields: ...)引用。 - @relation
- 关系属性,声明外键映射:
fields(本表外键列)、references(对表被引用列)、name(消歧多重关系)、onDelete/onUpdate(级联动作)。 - 隐式多对多(implicit M2M)
- 两侧都写列表字段,Prisma 自动创建并管理中间连接表,无需你显式建模,适合无额外字段的纯关联。
- 显式多对多(explicit M2M)
- 自己建一个连接 model(含两条一对多关系),可在中间表加额外字段(如
assignedAt、role)。 - 引用动作(referential action)
onDelete/onUpdate的取值:Cascade、Restrict、SetNull、SetDefault、NoAction,决定父行删除/改动时子行如何处置。
3.2 一对多(One-to-Many)
model User {
id Int @id @default(autoincrement())
name String
posts Post[] // 反向:一个 User 多篇 Post
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int // 外键列
}
"多"的一侧(Post)持有外键 authorId 与 @relation;"一"的一侧(User)只写反向列表 posts Post[]。
3.3 一对一(One-to-One)
model User {
id Int @id @default(autoincrement())
profile Profile? // 反向,可选
}
model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique // @unique 使其成为 1:1
}
1:1 的关键是 @unique
外键列加 @unique,才把"一对多"收窄为"一对一"。持有外键的一侧(Profile)是关系的"拥有方"。
3.4 隐式多对多
model Post {
id Int @id @default(autoincrement())
title String
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}
两侧都是列表,Prisma 自动建中间表 _CategoryToPost。写入用 connect:
await prisma.post.create({
data: {
title: "Prisma 101",
categories: { connect: [{ id: 1 }, { id: 2 }] },
},
});
3.5 显式多对多(带额外字段)
model User {
id Int @id @default(autoincrement())
members Membership[]
}
model Team {
id Int @id @default(autoincrement())
members Membership[]
}
model Membership {
user User @relation(fields: [userId], references: [id])
userId Int
team Team @relation(fields: [teamId], references: [id])
teamId Int
role String @default("member") // 额外字段
joinedAt DateTime @default(now())
@@id([userId, teamId])
}
3.6 自引用关系
// 评论树:一条评论可有父评论与多条子评论
model Comment {
id Int @id @default(autoincrement())
text String
parent Comment? @relation("Thread", fields: [parentId], references: [id])
parentId Int?
children Comment[] @relation("Thread")
}
多重关系用 name 消歧
同两个 model 间存在多条关系(或自引用)时,必须在 @relation("名字") 里给关系命名,让 Prisma 知道哪根字段配对哪根。
3.7 级联删除与引用动作
| 动作 | 父行删除时 |
|---|---|
| Cascade | 连带删除所有子行 |
| Restrict | 若有子行则阻止删除(报错) |
| SetNull | 把子行外键置为 NULL(外键须可空) |
| SetDefault | 把子行外键设为默认值 |
| NoAction | 不做处理,交给数据库约束 |
author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade)
3.8 小结与下一步
- 关系需两侧声明:一侧持外键 +
@relation,另一侧写反向字段。 - 1:1 = 外键加
@unique;隐式 M2M 自动建中间表,显式 M2M 可带额外字段。 - 自引用与多重关系用
@relation("name")消歧。 - 下一章讲 Prisma Migrate——把 schema 变更安全落地到数据库。