概要
MITライセンスに基づいて翻訳・公開いたします。
- 英語APIドキュメント:
ActiveRecord::Migration
- Railsバージョン: 6.0.0
- サイト: Ruby on Rails API
- ライセンス: rails/MIT-LICENSE at master · rails/rails
Rails API: ActiveRecord::Migration
(翻訳)
複数の物理データベースで使われるスキーマの成長をマイグレーションで管理できます。マイグレーションは、新しい機能を使うためにローカルデータベースにフィールドを追加するときによく起こる問題を解決する方法のひとつですが、変更点を他の開発者やproductionサーバーにプッシュする方法までは関知しません。マイグレーションを用いることで、自己完結するクラスの変換方法を(Gitなどの)バージョン管理システムにチェックインして記述し、別のデータベースに対して1バージョン前、2バージョン前、5バージョン前のものを実行できるようになります。
シンプルなマイグレーションの例を以下に示します。
class AddSsl < ActiveRecord::Migration[5.0]
def up
add_column :accounts, :ssl_enabled, :boolean, default: true
end
def down
remove_column :accounts, :ssl_enabled
end
end
上のマイグレーションは、accountsテーブルに:boolean
フラグを追加し、マイグレーションが巻き戻されるときには削除します。また、あらゆるマイグレーションでマイグレーションの実装や削除を行うのに必要な変換を記述するup
メソッドとdown
メソッドの使い方も示されています。これらのメソッドは、add_column
やremove_column
のようなマイグレーション固有のメソッドはもちろん、変換で必要なデータを生成する通常のRubyコードも含められます。
データの初期化が必要となる、さらに複雑なマイグレーションの例を以下に示します。
class AddSystemSettings < ActiveRecord::Migration[5.0]
def up
create_table :system_settings do |t|
t.string :name
t.string :label
t.text :value
t.string :type
t.integer :position
end
SystemSetting.create name: 'notice',
label: 'Use notice?',
value: 1
end
def down
drop_table :system_settings
end
end
このマイグレーションでは最初にsystem_settings
テーブルを追加し、続いて、そのテーブルが依存しているActive Recordを用いる最初の行を作成します。より高度なcreate_table
も用いて、テーブルの完全なスキーマを指定する構文を1つのブロック呼び出しで指定できます。
注: このAPIドキュメントには上述の
SystemSetting.create
のようにマイグレーション内でモデルを呼び出すコード例がいくつかありますが、そのモデルが将来削除またはリネームされるとdb:migrate
やdb:migrate:reset
が失敗する可能性があるので、マイグレーション内でのモデル呼び出しは避けるべきという指摘がBPS Webチームでありました。以下の2つの記事もご覧ください。
参考: How to use models in your migrations (without killing kittens) - The blog of makandra
利用可能な変換
作成用メソッド
create_join_table (table_1, table_2, options)
- 最初の2つの引数の辞書式順序(lexical order: いわゆるアルファベット順)を持つjoinテーブルを1つ作成する。詳しくは
ConnectionAdapters::SchemaStatements#create_join_table
を参照。 create_table(name, options)
name
という名前のテーブルを1つ作成して、そのテーブルオブジェクトをブロックで利用可能にしてカラムを足せるようにする。add_column
と同じフォーマットに従う。上述の例を参照。オプションハッシュは、テーブル作成の定義にappendされる"DEFAULT CHARSET=UTF-8"などのフラグメントに用いられる。add_column(table_name, column_name, type, options)
table_name
と呼ばれるテーブルにcolumn_name
と呼ばれる新しいカラムを1つ追加する。typeには:string
、:text
、:integer
、:float
、:decimal
、:datetime
、:timestamp
、:time
、:date
、:binary
、:boolean
のいずれかを指定する。デフォルト値は、{ default: 11 }
などのoptions
ハッシュを渡すことで指定できる。:limit
や:null
(`{ limit: 50, null: false }など)といったその他のオプションについて詳しくは、ActiveRecord::ConnectionAdapters::TableDefinition#columnを参照。add_foreign_key(from_table, to_table, options)
- 新しい外部キーを1つ追加する。
from_table
はキーのカラムを持つテーブル、to_table
は、参照される主キーを含むテーブルを表す。 add_index(table_name, column_names, options)
- 指定のカラム名を持つ新しいインデックスを1つ追加する。
:name
や:unique
({ name: 'users_name_index', unique: true }
など)、:order
({ order: { name: :desc } }
など)といったオプションもある。 add_reference(:table_name, :reference_name)
reference_name_id
(デフォルトでinteger)という新しいカラムを1つ追加する。詳しくはConnectionAdapters::SchemaStatements#add_reference
を参照。add_timestamps(table_name, options)
- タイムスタンプ(
created_at
とupdated_at
)カラムをtable_name
に追加する。
変更用メソッド
change_column(table_name, column_name, type, options)
- 指定のカラムを、
add_column
と同じパラメータを用いて別のタイプに変更する。 change_column_default (table_name, column_name, default_or_changes)
table_name
のdefault_or_changes
で定義されたcolumn_name
のデフォルト値を設定する。:from
と:to
を含むハッシュをdefault_or_changes
として渡すと、マイグレーションでこの変更をリバース可能にできる。change_column_null (table_name, column_name, null, default = nil)
column_name
にNOT NULL制約を設定または削除する。null
フラグは、値にNULL
を使ってよいかどうかを指定する。詳しくはConnectionAdapters::SchemaStatements#change_column_nullを参照。change_table(name, options)
name
というテーブルへのカラム変更(ALTER)を行える。このメソッドによって、tableオブジェクトをブロックで利用できるようにしてカラムやインデックスや外部キーを追加または削除できるようにします。rename_column(table_name, column_name, new_column_name)
- カラムをリネームします。タイプや内容は変更しません。
rename_index(table_name, old_name, new_name)
- インデックスをリネームします。
rename_table(old_name, new_name)
- テーブルを
old_name
からnew_name
にリネームします。
削除用メソッド
drop_table(name)
name
というテーブルをDROPします。drop_join_table(table_1, table_2, options)
- 引数で指定されたJOINテーブルをDROPします。
remove_column(table_name, column_name, type, options)
table_name
というテーブルからcolumn_name
というカラムを削除します。remove_columns(table_name, *column_names)
- 指定のカラムをテーブル定義から削除します。
remove_foreign_key (from_table, to_table = nil, **options)
- 指定の外部キーを
table_name
というテーブルから削除します。 remove_index(table_name, column: column_names)
- インデックスを
column_names
で指定して削除します。 remove_index(table_name, name: index_name)
- インデックスを
index_name
で指定して削除します。 remove_reference (table_name, ref_name, options)
table_name
にある参照をref_name
で指定して削除します。remove_timestamps (table_name, options)
- タイムスタンプのカラム(
created_at
とupdated_at
)をテーブル定義から削除します。
リバースできない変換
変換によっては、破壊的な操作を行うためリバースできないものがあります。そのようなマイグレーションはdown
メソッドでActiveRecord::IrreversibleMigration
例外が発生します。
Railsの中でマイグレーションを実行する
Railsパッケージには、マイグレーションの作成や適用を支援するさまざまなツールがあります。
新しいマイグレーションの生成には以下のコマンドを使えます。
rails generate migration MyNewMigration
MyNewMigrationはマイグレーションの名前です。この場合ジェネレータによってdb/migrate/
ディレクトリの下にタイムスタンプ_my_new_migration.rb
というファイルが作成されます。このタイムスタンプは、マイグレーションが生成された日時をUTC形式で表したものです。
テーブルにフィールドを追加するマイグレーションを生成するための、特殊なショートカット構文が利用できます。
rails generate migration add_fieldname_to_tablename fieldname:string
上は以下のような内容のタイムスタンプ_add_fieldname_to_tablename.rb
ファイルを生成します。
class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
def change
add_column :tablenames, :fieldname, :string
end
end
現在設定されているデータベースへのマイグレーションを実行するには、rails db:migrate
コマンドを使います。これによってペンディング中のマイグレーションがすべて実行されてデータベースが更新され、schema_migrations
テーブルが作成されます(後述の「schema_migrations
テーブルについて」を参照)。schema_migrations
テーブルが見当たらない場合は、db:schema:dump
コマンドも実行してdb/schema.rbファイルを更新し、データベース構造と整合させます。
データベースを以前のバージョンのマイグレーションにロールバックするには、rails db:rollback VERSION=X
を使います。このX
はダウングレード先のバージョンです。直近のいくつかのマイグレーションにロールバックしたい場合は、STEP
オプションを用いることもできます。rails db:rollback STEP=2
を実行すると、直近の2つのマイグレーションをロールバックします。
複数のマイグレーションのいずれかでActiveRecord::IrreversibleMigration
例外が発生すると、そのステップは失敗します。その場合は手動での作業が必要です。
サポートされるデータベース
現在マイグレーションをサポートしているのは、MySQL、PostgreSQL、SQLite、SQL Server、Oracleです(DB2を除くこれらすべてをサポートします)。
さまざまなマイグレーションの例
マイグレーションによってスキーマが変更されるとは限りません。たとえば、以下のようにデータの修正のみを行うマイグレーションもあります。
class RemoveEmptyTags < ActiveRecord::Migration[5.0]
def up
Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
end
def down
# not much we can do to restore deleted data
raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
end
end
次のように、(down
ではなく)up
の場合にカラムを削除するマイグレーションもあります。
class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
def up
remove_column :items, :incomplete_items_count
remove_column :items, :completed_items_count
end
def down
add_column :items, :incomplete_items_count
add_column :items, :completed_items_count
end
end
次のように、マイグレーションで直接抽象化を行わず、SQLで何かを実行する必要が生じることもありえます。
class MakeJoinUnique < ActiveRecord::Migration[5.0]
def up
execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
end
def down
execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
end
end
モデルのテーブルを変更した後でそのモデルを使う場合の注意
マイグレーションでカラムを1つ追加し、すぐにそれを使いたいことがあります。このような場合、以下の例のようにBase#reset_column_information
を呼んで、新しいカラムの追加後にそのモデルで最新のカラムデータを確実に利用できるようにする必要があります。
class AddPeopleSalary < ActiveRecord::Migration[5.0]
def up
add_column :people, :salary, :integer
Person.reset_column_information
Person.all.each do |p|
p.update_attribute :salary, SalaryCalculator.compute(p)
end
end
end
コンソール出力を制御する
デフォルトのマイグレーションでは、実行中の操作をその都度コンソールに詳細出力し、各ステップに要した時間を示すベンチマークも出力に含めます。
出力を止めるには、ActiveRecord::Migration.verbose = false
を設定します。
say_with_time
メソッドを使えば、以下のようにメッセージやベンチマークに独自のメッセージを追加することもできます。
def up
...
say_with_time "salaryを更新中..." do
Person.all.each do |p|
p.update_attribute :salary, SalaryCalculator.compute(p)
end
end
...
end
ブロックが完了すると、そのブロックのベンチマークとともに「salaryを更新中...」というフレーズが出力されます。
タイムスタンプ付きのマイグレーション
Railsがデフォルトで生成するマイグレーションファイルは以下のようになります。
20080717013526_your_migration_name.rb
ファイル名の冒頭は、生成時のタイムスタンプ(UTC)を表します。
この形式ではなく、ファイル名の冒頭に数値を使いたい場合は、application.rbで以下を設定することでマイグレーションのタイムスタンプをオフにできます。
config.active_record.timestamped_migrations = false
リバース可能なマイグレーション
リバース可能なマイグレーションとは、マイグレーションをdown
で取り消す方法を自動認識するマイグレーションのことです。リバース可能なマイグレーションではup
する方法を指定するだけで、down
コマンドで実行すべき内容をマイグレーションシステムが認識してくれます。
リバース可能なマイグレーションを定義するには、以下のようにマイグレーションでchange
メソッドを定義します。
class TenderloveMigration < ActiveRecord::Migration[5.0]
def change
create_table(:horses) do |t|
t.column :content, :text
t.column :remind_at, :datetime
end
end
end
上のマイグレーションはupのときにはhorses
テーブルを作成し、downのときにhorses
テーブルをDROPする方法を自動認識します。
コマンドの中には、リバースできないものもあります。そのような場合にupとdownの方法を定義しておきたい場合は、従来と同様にup
メソッドとdown
メソッドを定義すべきです。
マイグレーションがdownするときにリバースできないコマンドでは、ActiveRecord::IrreversibleMigration
例外が発生します。
リバース可能なコマンドリストについてはActiveRecord::Migration::CommandRecorder
を参照してください。
トランザクショナルなマイグレーション
DDL(データ定義言語)のトランザクションをサポートするデータベースアダプタでは、すべてのマイグレーションが自動的に1つのトランザクション内にラップされます。クエリによってはトランザクションの中では実行できないものがありますが、そのような場合は次のようにしてトランザクションを自動的にオフにできます。
class ChangeEnum < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
execute "ALTER TYPE model_size ADD VALUE 'new_value'"
end
end
ただし、self.disable_ddl_transaction!
を使うマイグレーションの内部であっても独自のトランザクションをオープンできることを忘れてはいけません。
関連記事
/hachi8833/2021_12_17/77512