Node.jsのORMである Sequelize
はその基となったActiveRecordと同様にDBのマイグレーション機構も備わっています。 Sequelize
のマイグレーション操作についてはまとまった記事が見当たらなかったので後世のために設定から基本操作までここに記しておこうと思います。
ここでは接続先のDBとしてPostgreSQLを利用していますが、MySQL、SQLiteでも同じ操作でmigrationが実行できます。
- 実行環境・ライブラリのバージョン情報
node: 7.5.0
sequelize: 3.30.2
sequelize-cli: 2.5.1
追記(2017年2月19日)
DB接続の準備
まず、動作確認用のプロジェクトを作成します。
mkdir migration-test
cd migration-test
npm init
sequelize
本体と PostgreSQL
を扱うためのライブラリをインストールします。
npm install sequelize --save
npm install pg --save
コマンドライン実行のセットアップ
コマンドラインから sequelize
のmigrationを実行できるように sequelize-cli
をインストールします。
npm install sequelize-cli --save
以下のようにプロジェクトにインストールされた sequelize
シェルを実行します。
node_modules/.bin/sequelize -v
グローバルインストールした場合は以下のように sequelize
だけで実行できます。
npm install sequelize-cli -g
sequelize -v
DB接続設定
以下のコマンドで config
ディレクトリとその直下に config.json
が生成されます。
sequelizeでのmigration実行では config.json
の設定を使用してDBに接続します。
node_modules/.bin/sequelize init
config.json
は生成直後は以下の状態です。
host
username
password
をファイルに直接記述するのは避けたいところです。
ちなみに dialect
は利用するDBの種類になります。
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
DB接続設定は環境依存情報なので環境変数から取得するのがセオリーです。
config.json
を以下のように書き換えます。
簡略化するために test
環境の設定を省きましたが必要ならば追加してください。
唐突に出現した use_env_variable
が気になる人は こちら のコードを追ってみてください。
DATABASE_URL
のところは任意の文字列でよいのですが、Heroku環境にデプロイする際に都合がよい ので DATABASE_URL
にしています。
{
"development": {
"use_env_variable": "DATABASE_URL"
},
"production": {
"use_env_variable": "DATABASE_URL"
}
}
環境変数に DATABASE_URL
を追加します。
PostgreSQLの場合の DATABASE_URL
の書式は以下のようになります。
[DATABASE_URL]
postgres://[user_name]:[password]@[host]:[port]/[database_name]
設定 | 値 |
---|---|
host | localhost |
user_name | user01 |
password | qazwsx |
port | 5432 |
database_name | dev |
接続設定が上記の値ならば DATABASE_URL
は以下のようになります。
[DATABASE_URL]
postgres://user01:qazwsx@localhost:5432/dev
export
コマンドで環境変数として登録します。
export DATABASE_URL=postgres://user01:qazwsx@localhost:5432/dev
新規テーブルの作成
雛型ファイルの生成
テーブル生成のための雛型は sequelize model:create
というコマンドで生成できます。
以下のようにオプションとして --name
--attributes
を指定すると素敵です。
カラム名をスネークケースにする場合は自動付加されるタイムスタンプ列のために --underscored
を指定します。 --underscored
を指定しない場合は createdAt
updatedAt
というカラム名になります。
node_modules/.bin/sequelize model:create --name user --underscored --attributes name:string,birth:date,country_code:integer
migrations
フォルダ下に (実行日時)-create-user.js
というファイル名のJavaScriptファイルが生成されます。 Ruby on Rails
に慣れている人なら見覚えのある形式だと気づくかもしれません。
必要に応じて allowNull
や default
値を設定します。
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
birth: {
type: Sequelize.DATE
},
country_code: {
type: Sequelize.INTEGER
},
created_at: {
allowNull: false,
type: Sequelize.DATE
},
updated_at: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('users');
}
};
models
フォルダ下には user.js
が生成されます。こちらはアプリケーション実行時に利用されるものです。
'use strict';
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
name: DataTypes.STRING,
birth: DataTypes.DATE,
country_code: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return User;
};
migrationの実行
db:migrate
コマンドで生成したスクリプトをDBに適用します。
--env
オプションを指定するとその環境のみに適用されます。
指定しない場合は config.json
に記述されたすべての環境に適用されるので普通は --env
を指定しますね。
node_modules/.bin/sequelize db:migrate --env development
pgAdminで確認してみると users
テーブルと SequelizeMeta
テーブルが作成されているのが分かります。 SequelizeMeta
テーブルにはmigrationファイルのファイル名のレコードが作成されます。
ロールバックする場合は以下のコマンドを実行します。
ロールバックで指定した処理が実行され、 SequelizeMeta
の該当のレコードが削除されます。
node_modules/.bin/sequelize db:migrate:undo --env development
既存テーブルへのカラム追加
雛型ファイルの生成
以下のコマンドで既存テーブルのマイグレーションスクリプトの雛型を生成できます。
migrations
フォルダ下に (実行日時)-user.js
というファイル名のJavaScriptファイルが作成されます。 --name
オプションを指定しないと (実行日時)-unnamed-migration.js
というファイル名です。
migration:generate
も migration:create
の結果はどちらも同じなので使いやすい方を利用しましょう。個人的には migration:generate
の方がしっくりきます。
node_modules/.bin/sequelize migration:generate --name user
node_modules/.bin/sequelize migration:create --name user
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
// コメント部分は省略しています
},
down: function (queryInterface, Sequelize) {
// コメント部分は省略しています
}
};
以前に作成した users
テーブルに email
と prefecture_code
カラムを追加します。
queryInterface.addColumn
メソッドにテーブル名、カラム名とカラムの詳細設定を渡します。
残念ながらPostgreSQLは任意のカラムの後にカラムを追加することはできず、最後尾に追加することしかできません。そのため before
after
オプションがあるのですが機能しません。MySQLを利用する場合に検討してください。
down
にはロールバック用のカラム削除処理を記述します。
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return [
queryInterface.addColumn('users', 'email', {
type: Sequelize.STRING,
after: 'country_code' // PostgreSQLでは無効なオプションです
}),
queryInterface.addColumn('users', 'prefecture_code', {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
after: 'email' // PostgreSQLでは無効なオプションです
})
];
},
down: function (queryInterface, Sequelize) {
return [
queryInterface.removeColumn('users', 'email'),
queryInterface.removeColumn('users', 'prefecture_code')
];
}
};
migrationの実行
テーブル作成時と同じく、 sequelize db:migrate
で適用します。
node_modules/.bin/sequelize db:migrate --env development
ロールバック処理も同様です。
node_modules/.bin/sequelize db:migrate:undo --env development
カラム追加のマイグレーション実行後の users
テーブルの構造です。
インデックスの追加
既存のテーブルにインデックスを追加する方法です。カラム追加の時と同じようにまずは雛型のファイルを作成します。
node_modules/.bin/sequelize migration:generate --name user
既存の name
列のインデックスとユニーク制約を追加、 新規に last_login_at
列を追加してインデックスにします。
インデックスの追加は addIndex
メソッドを使用します。テーブル名と対象となる列の組み合わせ、インデックスの詳細を設定します。
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return [
queryInterface.addIndex(
'users',
['name'],
{
indexName: 'users_name_index',
indicesType: 'UNIQUE' // ユニークインデックスになります
}
),
queryInterface.addColumn('users', 'last_login_at', {
type: Sequelize.DATE
}),
queryInterface.addIndex(
'users',
['last_login_at'],
{
indexName: 'users_last_login_at_index' // こちらは単なるインデックス
}
),
];
},
down: function (queryInterface, Sequelize) {
return [
queryInterface.removeIndex('users', 'users_name_index'),
queryInterface.removeIndex('users', 'users_last_login_at_index')
];
}
};
sequelize db:migrate
でマイグレーションを実行すると2つのインデックスが作成されます。
複合主キーの記述方法
複数の列で一意になる複合主キーが必要な場合は主キーを構成する列全てに primaryKey: true
を記述します。以下のテーブルは user_id
と friend_id
の2つで複合主キーとなります。
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('friends', {
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true
},
friend_id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true
},
created_at: {
type: Sequelize.DATE,
allowNull: false
},
updated_at: {
type: Sequelize.DATE,
allowNull: false
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('friends');
}
};
マイグレーションを実行すると複合主キーを持つ friends
テーブルが作成されます。
コマンドまとめ
ここで紹介したコマンドの一覧です。
内容 | コマンド |
---|---|
モデル+雛型作成 | sequelize model:create --name [モデル名] --underscored --attributes [カラム名:データ型] |
雛型作成 | sequelize migration:generate --name [ファイルの接尾辞] |
マイグレーション実行 | sequelize db:migrate --env [対象の環境] |
巻き戻し | sequelize db:migrate:undo --env [対象の環境] |
DB上級者向けのTIPS
誤ったマイグレーション操作のために sequelize db:migrate:undo
ではどうにもならくなった場合は SequelizeMeta
のレコードを直接削除する、追加するなどで適用履歴を操作することが可能です。
あくまで SequelizeMeta
の履歴管理の仕組を把握している、かつSQLに長けた上級者向けの技です。