📐

Prismaでリレーションを定義する

2022/08/13に公開

はじめに

Prismaでリレーションを定義する際のメモになります
基本的にはPrismaのドキュメントを意訳したものになります

概要

Prismaにおける下記のリレーションの定義方法についてのメモ

  • 1対1(One to One)のリレーション
  • 1対多(One to Many)のリレーション
  • 多対多(Many to Many)のリレーション

1対1(One to One)のリレーション

model User {
  id      Int      @id @default(autoincrement())
  email   String
  profile Profile?
}

model Profile {
  id     Int  @id @default(autoincrement())
  user   User @relation(fields: [userId], references: [id]) // fields: [userId] = Profile.userId, references: [User.id]
  userId Int // relation scalar field (used in the `@relation` attribute above)
}
  • Userテーブル側
    • profile Profile? としてProfileモデルへの接続を定義する(Profileはオプショナル型)
  • Profileテーブル側
    • user User @relation(fields: [userId], references: [id]) としてUserモデルへの接続を定義
    • 上記の fields は自身のモデル(Profile)の項目、references にはProfileからみた参照先のモデル(User)の項目をいれる
    • 実際に参照を持つ項目として userId Int を定義する

ポイントは、
接続する側のモデル/接続される側のモデル両方のモデルにリレーションを定義する 必要があります

id以外への項目にリレーションを定義する場合

id以外の項目にリレーションを定義する場合、
ProfileモデルからみたUserモデルが一意になるように、参照先の項目に @unique をつけます

model User {
  id      Int      @id @default(autoincrement())
  email   String   @unique // <-- add unique attribute
  profile Profile?
}

model Profile {
  id     Int  @id @default(autoincrement())
  user   User @relation(fields: [userId], references: [email])
  userId Int  @unique // relation scalar field (used in the `@relation` attribute above)
}

https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-one-relations

1対多(One to Many)のリレーション

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
}

基本的には1対1(One to One)のリレーションと同じ考え方になります
1Userあたり複数のPostを持つことになるため、
Userモデル側のリレーションは posts Post[] として配列の型で定義します

https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-many-relations

多対多(Many to Many)のリレーション

多対多(Many to Many)のリレーション定義は、
①明示的(explicit)な定義
②暗黙的(implicit)な定義
の2通りの定義方法があります

明示的なリレーション定義

明示的なリレーション定義では、
接続したいモデルA(Post)とモデルB(Category)の間に、
中間モデル(CategoriesOnPosts) を指定します
型は1対多の時同様に、配列の型で定義します

model Post {
  id         Int                 @id @default(autoincrement())
  title      String
  categories CategoriesOnPosts[]
}

model Category {
  id    Int                 @id @default(autoincrement())
  name  String
  posts CategoriesOnPosts[]
}

model CategoriesOnPosts {
  post       Post     @relation(fields: [postId], references: [id])
  postId     Int // relation scalar field (used in the `@relation` attribute above)
  category   Category @relation(fields: [categoryId], references: [id])
  categoryId Int // relation scalar field (used in the `@relation` attribute above)
  assignedAt DateTime @default(now())
  assignedBy String

  @@id([postId, categoryId])
}

上記に中間モデル(CategoriesOnPosts)の中で、
モデルA(Post)とモデルB(Category)への接続を定義します
また、

@@id([postId, categoryId])

として、それぞれを参照する項目を @@id に指定します
※マルチフィールドリレーション

暗黙的なリレーション定義

暗黙的なリレーション定義では、中間モデルの記載がない分
明示的なリレーション定義よりもシンプルな記載となります

model Post {
  id         Int        @id @default(autoincrement())
  categories Category[]
}

model Category {
  id    Int    @id @default(autoincrement())
  posts Post[]
}

暗黙的なリレーション定義では、モデルの記載がシンプルになる分、
下記のようないくつかのルールを意識する必要があります

  • リレーション テーブルにConventions for relation tablesを使用すること
  • リレーションに明示的に名前をつけて管理したい時以外は、 @relation は不要
    • @relation を使う場合は、 references, fields, onUpdate or onDelete の属性は使えない
  • マルチフィールドリレーション及び @id の変わりに @unique を使用することはできない

Rules for defining an implicit m-n relation

Implicit m-n relations:

  • Use a specific convention for relation tables

  • Do not require the @relation attribute unless you need to disambiguate relations with a name, e.g. @relation("MyRelation") or @relation(name: "MyRelation").

  • If you do use the @relation attribute, you cannot use the references, fields, onUpdate or onDelete >arguments. This is because these take a fixed value for implicit m-n relations and cannot be changed.

  • Require both models to have a single @id. Be aware that:
    You cannot use a multi-field ID
    You cannot use a @unique in place of an @id

参考: 公式ドキュメント

上記のうち、1つ目のConventions for relation tablesについては、

npx prisma migrate

を使用を使用しない場合(手動でSQLを生成する場合)は意識する必要がありそうです

https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations

まとめ

  • リレーション定義は接続する側のモデル・接続される側のモデルの両方に記載する
  • n対多のリレーションを定義する際は モデル名[]とする
  • 多対多のリレーション定義は明示的指定と暗黙的指定の2種類がある

参考

Prisma 公式ドキュメント
One-to-one relations
One-to-many relations
Many-to-many relations
Prismaの使い方を学ぶ
PrismaでMany-to-manyのテーブルを定義する

Discussion