少し前のブログ(これ や これ)で書いたように「Prismaを、もう少し学ばなくては」を実践しています。
私は長年Ruby on Railsを使ってきたので有名なORMであるActive Recordに付いては良く知ってます。 したがってPrismaは簡単にわかると思っていましたが、調べてみると大きく異なるORMのようです。
今回は、マイグレーション(migration) に付いて書きます。
Bing Image Creatorが生成した画像を使っています
まずはインストールと設定の手順
Prismaを使う時には、毎度インストールや設定を調べているのでまとめておきます。
インストール
本番環境で使われるのはPrismaクライアント・ライブラリー@prisma/clientのみで、マイグレーション等のツールであるprisma は開発環境のみにインストールします。
$ npm install @prisma/client
$ npm install --save-dev prisma
設定手順
npx prisma init
でPrisma用ディレクトリーや(空の)スキーマ・ファイルを生成- スキーマ・ファイル
prisma/schema.prisma
を設定- 今回はPostgreSQLを使います
- shadowDatabaseUrlに付いては少し前のブログに書きました
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
- 必要ならPostgreSQLのアカウントやデータベースを作成
.env
ファイルにPostgreSQL接続URLを記述
DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres"
SHADOW_DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres_shadow"
npx prisma generate
でRDB接続用ライブラリーを作成./node_modules/.prisma/client/
にライブラリーや設定ファイルが作成されます
Prismaのマイグレーション
今回はサンプルのテーブルを作成し、修正していった手順を書きます。データは以前も使ったTheCatAPIとWorld countriesを使っています。
まずRailsとの違いをあげておきます、
- Railsではテーブル作成(追加)はモデル作成のジェネレーターを使って作りますが、Prismaではスキーマ・ファイル
prisma/schema.prisma
で行います - Railsではテーブル変更はマイグレーション・ジェネレーターで変更用マイグレーションファイルを作りますが、Prismaではスキーマ・ファイル
prisma/schema.prisma
を直接変更します- PrismaではマイグレーションコマンドがRDBに依存したテーブル作成・変更用SQLファイルを生成します
- ある意味で、マイグレーションの履歴はGitにしか残りません
- Railsのマイグレーションファイルは独自DSL(Rubyメソッド)ですが、Prismaもスキーマ・ファイル
prisma/schema.prisma
は独自の文法です - Railsでは開発サイクルが始まってからのsqlite3からMySQLへの変更等したりできますが、Prismaではできません(正確にはマイグレーション用SQLファイルが使えなくなってしまいます)
マイグレーションの公式ドキュメントはPrisma Migrate - Getting started、Prisma Migrate - Overview … に書かれています、日本語の記事も多数あります。
1. 始め
猫(cats)のテーブルを作ります。model Cat {...}
からテーブルが作成されます。
- prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
model Cat {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
country_code String @db.VarChar(2)
reference_image_id String @db.VarChar(255)
temperament String @db.VarChar(255)
description String @db.Text
}
説明
@id
主キー@default(autoincrement())
デフォルト値の定義、autoincrement()
で連番が生成されます@db.VarChar()
文字型の長さを指定しています- この指定がない場合のStringのデフォルトはRDB毎に決まっています →これ
@db.Text
制限無し可変長文字列
これをnpx prisma migrate dev
コマンドでマイグレーションすると以下のようなSQLが生成され、Cat
というテーブルが作成されました。さらに主キー用にCat_id_seq
も生成されました。
CREATE TABLE "Cat" (
"id" SERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
"country_code" VARCHAR(2) NOT NULL,
"reference_image_id" VARCHAR(255) NOT NULL,
"temperament" VARCHAR(255) NOT NULL,
"description" TEXT NOT NULL,
CONSTRAINT "Cat_pkey" PRIMARY KEY ("id")
);
2. NULLカラム
1の定義では全てのカラムがNOT NULL
になってしまいます。NULLを許すカラム定義は以下のように定義します。(TypeScriptのオブジェクトではプロパティー名の後ろに?
を付けますが、Prismaでは型の後ろに?
を付けます)
・・・省略・・・
model Cat {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
country_code String? @db.VarChar(2)
reference_image_id String? @db.VarChar(255)
temperament String? @db.VarChar(255)
description String? @db.Text
}
これをマイグレーションすると以下のようなSQLが生成され実行されました。
ALTER TABLE "Cat"
ALTER COLUMN "country_code" DROP NOT NULL,
ALTER COLUMN "reference_image_id" DROP NOT NULL,
ALTER COLUMN "temperament" DROP NOT NULL,
ALTER COLUMN "description" DROP NOT NULL;
3. テーブル名、カラム名の変更
Catテーブルから取得したデータのオブジェクトのプロパティー名はJavaScriptのルール通りキャメルケース(例 country_code
→ countryCode
)に変更します、ただしテーブルのカラム名はそのままにします。またテーブル名はRailsに合わせ小文字・複数形でcats
に変更しました。
- prisma/schema.prisma
・・・省略・・・
model Cat {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
countryCode String? @db.VarChar(2) @map("country_code")
referenceImageId String? @db.VarChar(255) @map("reference_image_id")
temperament String? @db.VarChar(255)
description String? @db.Text
@@map("cats")
}
説明
@map("country_code")
モデルと違うカラム名を付けます@@map("cats")
モデル名と違うテーブル名を付けます
これをマイグレーションすると以下のようなSQLが生成され実行されました。テーブルを作り直してますね!
このような変更の場合は Are you sure you want to create and apply this migration? のような確認が表示されます。
DROP TABLE "Cat";
CREATE TABLE "cats" (
"id" SERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
"country_code" VARCHAR(2) NOT NULL,
"reference_image_id" VARCHAR(255) NOT NULL,
"temperament" VARCHAR(255),
"description" TEXT,
CONSTRAINT "cats_pkey" PRIMARY KEY ("id")
);
4. テーブル追加
国名コード(例、日本はJA
)のテーブルを追加しました。
- prisma/schema.prisma
・・・省略・・・
model Cat {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
countryCode String? @db.VarChar(2) @map("country_code")
referenceImageId String? @db.VarChar(255) @map("reference_image_id")
temperament String? @db.VarChar(255)
description String? @db.Text
@@map("cats")
}
model Country {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
countryCode String @db.VarChar(2) @map("country_code")
@@map("countries")
}
とくに説明項目はありません、これをマイグレーションすると以下のようなSQLが生成され実行されました。
CREATE TABLE "countries" (
"id" SERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
"country_code" VARCHAR(2) NOT NULL,
CONSTRAINT "countries_pkey" PRIMARY KEY ("id")
);
5. catsテーブルとcountriesテーブルを関連させる
- prisma/schema.prisma
・・・省略・・・
model Cat {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
countryId Int? @map("country_id")
referenceImageId String? @db.VarChar(255) @map("reference_image_id")
temperament String? @db.VarChar(255)
description String? @db.Text
country Country? @relation(fields: [countryId], references: [id])
@@map("cats")
}
model Country {
id Int @id @default(autoincrement())
name String @db.VarChar(255)
countryCode String @db.VarChar(2) @map("country_code")
cats Cat[]
@@map("countries")
}
@relation(fields: [countryId], references: [id])
はcatsテーブルはcountry_idカラムを外部キーとして、countriesテーブルのidカラムに関連します- catsテーブルのcountry_codeカラムは削除しました
- Catモデルのオブジェクトは、countryプロパティーで対応するCounryモデルをアクセスできます
- また、Countryモデルのオブジェクトは、catsプロパティーで対応するCatモデルをアクセスできます
これをマイグレーションすると以下のようなSQLが生成され実行されました。
ALTER TABLE "cats"
DROP COLUMN "country_code",
ADD COLUMN "country_id" INTEGER;
ALTER TABLE "cats" ADD CONSTRAINT "cats_country_id_fkey" FOREIGN KEY ("country_id")
REFERENCES "countries"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Prismaのマイグレーションのまとめ
- Prismaではスキーマ・ファイルを直接変更しながらマイグレーションを行います。
- したがってGit等で履歴管理しないと過去の状態が判らなくなります
- もちろん、マイグレーション毎にSQL文が生成されるので手間をかければ判らなくはないです
- ただし
ALTER TABLE
SQL文などのテーブルをメンテナンスするSQL文の知識が無くても、テーブルの変更等が簡単に行えます- (Railsでも
ALTER TABLE
SQL文の詳細は判らなくても変更できますが、ある程度はSQL文の知識が必要だと思います)
- (Railsでも
- マイグレーションの内容によってはテーブル内のデータが消えてしまう事があります。その際には警告が表示されます。
- 開発サイクルが始まってからのRDB変更(例、sqlite3からMySQLへの変更)は大変です
- 過去に生成されたマイグレーション用SQL文は使えなくなります
PrismaのマイグレーションはRuby on Railsとは大きく違います。プロジェクトチーム内にPrisma経験者がいない場合は、サンプル・プロジェクト等でマイグレーションを体験する事をお勧めします。