概要
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