今天要來介紹 relation
,在 prisma
中你可以定義兩個 model
去關聯彼此的關係,舉個例子以下是兩個 model
User
跟 post
,那因為一個 User
可以有很多個 Post
所以 User
跟 Post
是一個一對多的關係,在 prisma
中會透過 @relation
去表示兩個 model
的關係是一對多、ㄧ對一還是多對多等。
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
name String
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
}
這邊補充一個小知識,在 Prisma
中透過 @relation
定義的 fields
如上方的 author
這個欄位,實際上並不會出現在 DB
的欄位中,他只是讓 Prisma
知道兩個 Model
之間的關聯性,讓 Prisma client
知道要如何 generate
。
那至於 authorId
則是會實際存在 DB
中,透過 @relation
去 referenced
,因為它代表著是一個 foreign key
去關聯 Post
跟 User
。
以下的圖就是一個簡單的關聯式 table
,代表著一對多得關係。
在 SQL
中,會使用 foreign key (FK)
在兩個表之間建立關係。FK
會存放在關聯的一側。
FK
會是在 Post
然後 key
是 authorId
PK
就是 User
的 id
,他需要給 authorId
讓 Post
可以關聯到 user
那在 prisma
則是透過 @relation
定義 PK
跟 FK
,所以才會需要加一個 key
,只是就像一開始說的 author
並不是一個真正的欄位。
author User @relation(fields: [authorId], references: [id])
在 prisma
你要 create
一筆有 relation
的 data
有兩種方式,一種如下:
create a user
然後讓 prisma
auto generate id
create
兩筆新的 post
然後 link
到 posts
const userAndPosts = await prisma.user.create({
data: {
posts: {
create: [
{ title: 'Prisma Day 2020' }, // Populates authorId with user's id
{ title: 'How to write a Prisma schema' }, // Populates authorId with user's id
],
},
},
})
那如果是要 link
到已經有的 post
則可以透過 connect
的寫法去對到 post.id
const result = await prisma.user.create({
data: {
posts: {
connect: [{ id: 8 }, { id: 9 }, { id: 10 }],
},
}
})
這時當你很開心的創建好資料後,想去查看 data
於是你透過 findUnique
去 get data
const getAuthor = await prisma.user.findUnique({
where: {
id: "20",
}
});
這時你會發現並沒有 return post
的資料,那是因為 prisma
預設不會 return
所有關於 relation
的 data
{id:0}
所以這邊你需要加上 include
來告訴 prisma
說你 return
的 data
要包含 post
的部分。
const getAuthor = await prisma.user.findUnique({
where: {
id: "20",
},
include: {
posts: true, // All posts where authorId == 20
},
});
這樣你才會拿到 post
的資料
{id:0,posts:[{id:0,authorId:0},{id:1,authorId:0}]}
另外如果你需要要重新綁定 post
的內容到 user
,則可以透過 nest object
的寫法,重新指定 user
需要綁定哪些 post
內容。
const updateAuthor = await prisma.user.update({
where: {
id: 20,
},
data: {
posts: {
connect: {
id: 4,
},
},
},
})
如果你得 post id
是錯的, prisma
會 throw
一下的 error
The required connected records were not found. Expected 1 records to be connected, found 0.
有一種情形是如果今天你不確定你得 post
是否存在,則可以透過 connectOrCreate
讓 prisma
自動幫你判斷,如果有直接 connect id
沒有就幫你 create
一筆資料,這樣你就可以減少重複的 post
資料出現。
const result = await prisma.user.create({
data: {
title: 'How to make croissants',
posts: {
connectOrCreate: {
where: {
name: 'post-1',
},
create: {
name: 'post-1',
},
},
},
},
include: {
posts: true,
},
})
如果你希望斷掉特定post
的 relation
則可以透過 disconnect
const result = await prisma.user.update({
where: {
id: 16,
},
data: {
posts: {
disconnect: [{ id: 12 }, { id: 19 }],
},
},
include: {
posts: true,
},
})
全清的話就直接給一個 []
const result = await prisma.user.update({
where: {
id: 16,
},
data: {
posts: {
set: [],
},
},
include: {
posts: true,
},
})
如果今天 post
跟 user
是一對一的關係,要 disconnect
直接給 true
就好
const result = await prisma.post.update({
where: {
id: 23,
},
data: {
author: {
disconnect: true,
},
},
include: {
author: true,
},
})
另外一個很特別的情況是,你需要重複關聯相同的 model
到不同的欄位,以下面的例子來看:
Post
可能會有不同的 User
種類 author
跟 pinnedBy
// NOTE: This schema is intentionally incorrect. See below for a working solution.
model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[]
pinnedPost Post?
}
model Post {
id Int @id @default(autoincrement())
title String?
author User @relation(fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation(fields: [pinnedById], references: [id])
pinnedById Int?
}
但對 prisma
來說會有一個問題,因為他不知道 writtenPosts
到底是關聯到 post.author
還是 post.pinnedBy
所以 prisma
會理解成四種情況
User.writtenPosts
↔ Post.author
+ Post.authorId
User.writtenPosts
↔ Post.pinnedBy
+ Post.pinnedById
User.pinnedPost
↔ Post.author
+ Post.authorId
User.pinnedPost
↔ Post.pinnedBy
+ Post.pinnedById
那為了釐清以上的問題,你可以指定 @relation
的 name
是什麼,然後到你要關聯的 PK model
告訴 prisma
你要用哪個 relation
的 name
model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}
model Post {
id Int @id @default(autoincrement())
title String?
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById Int? @unique
}
今天大概簡單說明 relation
的概念,明天我們繼續深入他~
✅ 前端社群 :
https://lihi3.cc/kBe0Y