メインコンテンツへスキップ

参照アクションのアップグレードパス

Prisma ORM バージョン 2.x では、一部のPrisma Client関数で関連レコードの削除を防止し、その動作を変更するためにPrismaスキーマで参照アクションを設定することはできません。

Prisma ORM バージョン 3.x 以降では、モデルのリレーションに参照アクションを明示的に設定することにより、レコードを削除または更新する際に何が起こるかを制御できます。アップグレード後、Prisma Client は参照アクションを強制しなくなり、データベースの外部キーに書き込まれたアクションが、レコードの削除または更新時の動作を定義します。

Prisma Migrate 3.x は、外部キー制約をデータベースに書き込む際に、Prisma Client が以前に行っていたアクションを新しいデフォルトとして使用します。

Prisma ORM 2.x の動作

必須リレーションでPrisma Clientを使用してdelete() または deleteAll() メソッドを呼び出すと、ランタイムチェックが実行され、関連オブジェクトを参照している場合、レコードの削除が防止されます。これにより、外部キーがどのように定義されていても、カスケード動作が防止されます

アップグレードしない場合、Prisma ORM 2の動作では参照アクションを一切設定できません。Prisma ORM 2.x のデフォルト参照アクションを参照してください

データベースで設定されたカスケード動作を実際に使用する必要がある場合、raw SQLクエリを使用して、複数の参照レコードを削除することができます。これは、Prisma Client が生のクエリに対してランタイムチェックを実行しないためです。

Prisma ORM 2.x のデフォルト参照アクション

以下は、Prisma Migrate バージョン 2.x を使用する際にデータベースの外部キーに書き込まれるデフォルトの参照アクションです。

項目オプションのリレーション必須のリレーション
onDeleteSetNullCascade
onUpdateCascadeCascade

データベースの参照アクションに加えて、Prisma Client バージョン 2.x では以下の動作が強制されます。

項目オプションのリレーション必須のリレーション
onDeleteSetNullRestrict
onUpdateCascadeCascade

アップグレードパス

アップグレード時にはいくつかのパスがあり、目的の結果に応じて異なる結果が得られます。

現在マイグレーションワークフローを使用している場合、prisma db pull を実行して、デフォルトがスキーマにどのように反映されているかを確認できます。必要に応じて、手動でデータベースを更新できます。

また、デフォルトの確認をスキップして、新しいデフォルト値でデータベースを更新するマイグレーションを実行することもできます。

イントロスペクションの使用

データベースをイントロスペクトすると、データベースレベルで設定された参照アクションがPrismaスキーマに反映されます。Prisma Migrate または prisma db push を使用してデータベーススキーマを管理してきた場合、これらは2.25.0 以前のデフォルト値である可能性が高いです。

イントロスペクションを実行すると、Prisma ORM はデータベース内のすべての外部キーをスキーマと比較し、SQLステートメントのON DELETE および ON UPDATE がデフォルト値と一致しない場合、それらはスキーマファイルに明示的に設定されます。

イントロスペクト後、スキーマ内の非デフォルトの条項を確認できます。確認すべき最も重要な条項はonDeleteで、これはバージョン 2.25.0 以前ではCascadeがデフォルトでした。

危険

delete() または deleteAll() メソッドを使用している場合、Prisma Clientで以前にランタイムでカスケード削除を防止していたセーフティネットが削除されたため、カスケード削除が実行されるようになります。コードを確認し、それに応じて調整を行ってください。

スキーマ内のonDelete: Cascadeのすべてのケースが意図した通りであることを確認してください。そうでない場合は、次のいずれかを行ってください。

  • Prismaスキーマを変更し、データベースを変更するためにdb pushまたはdev migrateを実行するか、または
  • ワークフローでprisma db pullのみを使用している場合は、基になるデータベースを手動で更新してください。

次の例ではカスケード削除が行われます。つまり、Userが削除されると、そのPostもすべて削除されます。

ブログスキーマの例

model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
}

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

マイグレーションの使用

マイグレーション (または prisma db push コマンド) を実行すると、新しいデフォルトがデータベースに適用されます。

情報

prisma db pullを初めて実行した場合とは異なり、新しい参照アクションの条項とプロパティは、Prisma VSCode拡張機能によってPrismaスキーマに自動的に追加されることはありません。新しいデフォルト以外のものを使用したい場合は、手動で追加する必要があります。

Prismaスキーマで参照アクションを明示的に定義することはオプションです。リレーションの参照アクションを明示的に定義しない場合、Prisma ORM は新しいデフォルトを使用します。

参照アクションはケースバイケースで追加できることに注意してください。これは、単一のリレーションにそれらを追加し、それ以外は何も手動で指定しないことでデフォルト設定のままにできることを意味します。

エラーの確認

バージョン 3.0.1 へのアップグレード (または referentialActions 機能フラグを有効にしたバージョン 2.26.0 以降) は、Prisma ORM は参照整合性を維持するためにdelete() または deleteMany() を使用したレコードの削除を防止していました。Prisma Client により、エラーコードP2014 を伴うカスタムランタイムエラーがスローされていました。

アップグレードは、Prisma ORM はランタイムチェックを実行しなくなります。代わりに、カスタム参照アクションを指定して、リレーション間の参照整合性を維持できます。

レコードの削除を防止するためにNoAction または Restrict を使用する場合、エラーメッセージは、バージョン 3.0.1 以降 (または referentialActions 機能フラグを有効にした 2.26.0) とそれ以前のバージョンとで異なります。これは、それらがPrisma Clientによってではなく、データベースによってトリガーされるようになったためです。予想される新しいエラーコードはP2003 であるため、コードを確認し、それに応じて調整を行う必要があります。

エラーをキャッチする例

次の例では、PostUser間の1対多のリレーションシップを持つ以下のブログスキーマを使用し、authorフィールドにRestrict参照アクションを設定しています。

これは、ユーザーが投稿を持っている場合、そのユーザー (およびその投稿) は削除できないことを意味します。

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
authorId String
}

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

アップグレード前は、投稿を持つユーザーを削除しようとすると、エラーコードP2014とそのメッセージを受け取ります。

「変更しようとしている内容は、{model_a_name} モデルと {model_b_name} モデル間の必須リレーション '{relation_name}' に違反します。」

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id',
},
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
console.log(error.message)
}
}
}
}

main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})

コードで正しいエラーをチェックしていることを確認するために、P2003 を探すようにチェックを修正してください。これは次のメッセージを返します。

「フィールド: {field_name} で外部キー制約に失敗しました」

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id'
}
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
if (error.code === 'P2003') {
console.log(error.message)
}
}
}
}

main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
© . All rights reserved.