今天要來繼續介紹 relation one to many 以下簡稱一對多 ,一對多代表的意思是一個 model 可以關聯 0 到多個 model 所以他也可以是 optional 如下:
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
User.posts 代表著 Post.author 的 annotated relation field ,透過 Post.authorId(FK) 關聯 User.id(PK),簡單來說上面的 schema 代表:
user 可以有 0 個至多個 posts
post 並需要有一個 user 最多也只有一個 user
所以才會是一對多的關係
和一對一的關聯情況一樣,你可以指定其他的欄位當作你關聯的 id ,只要你能確保資料的唯一性,以 user 為例子,我們把 email 指定成為一的欄位,對應個字的 post,調整 @relation 的 references 是 User.email,同時在 Post 的 @relation.fileds 也調整一下 fileds name 改成 authorEmail ,這樣才知道我們的 post 是關聯到 User.email
model User {
id Int @id @default(autoincrement())
email String @unique // <-- add unique attribute
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
authorEmail String
author User @relation(fields: [authorEmail], references: [email])
}
同樣你也可以組合成特殊的 id ,以 User 來說我們組合 firstName 跟 lastName
model User {
firstName String
lastName String
posts Post[]
@@id([firstName,lastName])
}
model Post {
id Int @id @default(autoincrement())
authorFirstName String // relation scalar field (used in the `@relation` attribute above)
authorLastName String // relation scalar field (used in the `@relation` attribute above)
author User @relation(fields: [authorLastName,authorFirstName], references: [firstName,lastName])
}
原生 SQL 語法如下,這邊要注意 Post.authorId 並沒有加上 Unique 的情況,這樣才能代表 User 跟 Post 是一對多,如果 Post.authorId 加上 Unique 變成 "authorId" integer NOT NULL Unique 這樣 User 跟 Post 的關係就變成一對一了~
CREATE TABLE "User" (
id SERIAL PRIMARY KEY
);
CREATE TABLE "Post" (
id SERIAL PRIMARY KEY,
"authorId" integer NOT NULL,
FOREIGN KEY ("authorId") REFERENCES "User"(id)
);
簡單來說一對一跟一對多最大的差別在於 foreign key 有沒有 UNIQUE 去定義他,一對一有,一對多沒有
上面說到以下面的例子來說一個 User 可以有 0 個至 N 個 Post ,Post 至少要有一個 User 且最多一個,如果你希望 Post 也可以不用指定 user 的話在 @relation 的地方加上 ? 就可以
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
所以在沒有加上 ? 前,create 一個 post 必須指定哪一個 user
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
這邊補充一下 relation 中要如何在 prisma filter 你要的 data ,簡單整理一下常用的 options
這邊我們會用 is 跟 isNot 去表示 relation 有沒有對應的內容,以下面的範例來說,我們想找尋 post.author 不是 Bob 這個人,同時他的 age 必須要大於 40 歲
const users = await prisma.post.findMany({
where: {
author: {
isNot: {
name: 'Bob',
},
is: {
age: {
gt: 40,
},
},
},
},
include: {
author: true,
},
})
-to-many 則有三個 some 、every、none 用法跟 js 的 array method 一樣概念會是:
| Requirement | Query option to use |
|---|---|
| "I want a list of every User that has at least one unpublished Post record" | some posts are unpublished |
| "I want a list of every User that has no unpublished Post records" | none of the posts are unpublished |
| "I want a list of every User that has only unpublished Post records" | every posts are unpublished |
所以這邊我要找尋 user.post 他的 views 並沒有大於 100 同時他的 likes必須大於 50
const users = await prisma.user.findMany({
where: {
posts: {
none: {
views: {
gt: 100,
},
},
every: {
likes: {
lte: 50,
},
},
},
},
include: {
posts: true,
},
})
這邊代表找到User 沒有 Post 的 Record
const usersWithZeroPosts = await prisma.user.findMany({
where: {
posts: {
none: {},
},
},
include: {
posts: true,
},
})
反過來因為 post 他是一對多的一那邊,所以可以直接用 null 去找尋沒有 user 的 post
const postsWithNoAuthor = await prisma.post.findMany({
where: {
author: null, // or author: { }
},
include: {
author: true,
},
})
那如果要找到 User 至少會有一篇 post 的話則用 some
const usersWithSomePosts = await prisma.user.findMany({
where: {
posts: {
some: {},
},
},
include: {
posts: true,
},
})
✅ 前端社群 :
https://lihi3.cc/kBe0Y