今天要來介紹 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