1 マイグレーションの概要
マイグレーションは、一貫した方法でデータベーススキーマを時間とともに変更する便利な方法です。RubyのDSLを使用してSQLを手動で書く必要がないため、スキーマと変更はデータベースに依存しないようになります。
各マイグレーションをデータベースの新しい「バージョン」と考えることができます。スキーマは何も含まれていない状態で始まり、各マイグレーションはテーブル、列、またはエントリを追加または削除してスキーマを変更します。Active Recordは、データベースの最新バージョンにスキーマを更新する方法を知っています。また、db/schema.rb
ファイルもデータベースの最新の構造に合わせて更新します。
以下はマイグレーションの例です。
class CreateProducts < ActiveRecord::Migration[7.1]
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
このマイグレーションは、products
というテーブルを追加し、name
という文字列の列とdescription
というテキストの列を追加します。id
という主キー列も暗黙的に追加されます。これはすべてのActive Recordモデルのデフォルトの主キーです。timestamps
マクロは、created_at
とupdated_at
という2つの列を追加します。これらの特別な列は、存在する場合にActive Recordによって自動的に管理されます。
前進する変更を定義することに注意してください。このマイグレーションが実行される前にはテーブルは存在しません。実行後、テーブルが存在します。Active Recordは、このマイグレーションを逆にする方法も知っています。このマイグレーションをロールバックすると、テーブルが削除されます。
スキーマを変更するステートメントをトランザクションでサポートしているデータベースでは、各マイグレーションはトランザクションでラップされます。データベースがこれをサポートしていない場合、マイグレーションが失敗した場合、成功した部分はロールバックされません。手動で変更をロールバックする必要があります。
注意: トランザクション内で実行できないクエリがあります。アダプタがDDLトランザクションをサポートしている場合は、単一のマイグレーションでそれらを無効にするためにdisable_ddl_transaction!
を使用できます。
1.1 反転不可能な操作を可能にする
Active Recordが逆にする方法を知らないマイグレーションを実行する場合は、reversible
を使用できます。
class ChangeProductsPrice < ActiveRecord::Migration[7.1]
def change
reversible do |direction|
change_table :products do |t|
direction.up { t.change :price, :string }
direction.down { t.change :price, :integer }
end
end
end
end
このマイグレーションは、price
列の型を文字列に変更し、マイグレーションが元に戻されると整数に戻します。direction.up
とdirection.down
に渡されるブロックに注目してください。
または、change
の代わりにup
とdown
を使用することもできます。
class ChangeProductsPrice < ActiveRecord::Migration[7.1]
def up
change_table :products do |t|
t.change :price, :string
end
end
def down
change_table :products do |t|
t.change :price, :integer
end
end
end
reversible
についての詳細は後述します。
2 マイグレーションの生成
2.1 スタンドアロンなマイグレーションの作成
マイグレーションは、db/migrate
ディレクトリにファイルとして保存されます。各マイグレーションクラスごとに1つのファイルがあります。ファイルの名前はYYYYMMDDHHMMSS_create_products.rb
の形式です。つまり、マイグレーションを識別するUTCタイムスタンプの後にアンダースコアが続き、マイグレーションの名前が続きます。たとえば、20080906120000_create_products.rb
はCreateProducts
クラスを定義し、20080906120001_add_details_to_products.rb
はAddDetailsToProducts
を定義する必要があります。Railsはこのタイムスタンプを使用して、実行するべきマイグレーションとその順序を決定します。したがって、他のアプリケーションからマイグレーションをコピーするか、自分でファイルを生成する場合は、その順序に注意してください。
もちろん、タイムスタンプを計算するのは楽しくありませんので、Active Recordはそれを作成するためのジェネレータを提供しています。
$ bin/rails generate migration AddPartNumberToProducts
これにより、適切な名前の空のマイグレーションが作成されます。
class AddPartNumberToProducts < ActiveRecord::Migration[7.1]
def change
end
end
このジェネレータは、ファイル名にタイムスタンプを前置するだけでなく、さらに多くのことができます。 命名規則と追加の(オプションの)引数に基づいて、マイグレーションを詳細化することもできます。
2.2 新しい列の追加
マイグレーション名が「AddColumnToTable」または「RemoveColumnFromTable」の形式であり、
列名と型のリストが続く場合、add_column
およびremove_column
ステートメントを含むマイグレーションが作成されます。
$ bin/rails generate migration AddPartNumberToProducts part_number:string
これにより、次のマイグレーションが生成されます。
class AddPartNumberToProducts < ActiveRecord::Migration[7.1]
def change
add_column :products, :part_number, :string
end
end
新しい列にインデックスを追加する場合も同様に行うことができます。
$ bin/rails generate migration AddPartNumberToProducts part_number:string:index
これにより、適切なadd_column
およびadd_index
ステートメントが生成されます。
class AddPartNumberToProducts < ActiveRecord::Migration[7.1]
def change
add_column :products, :part_number, :string
add_index :products, :part_number
end
end
魔法のように生成される列は1つに制限されません。例えば:
$ bin/rails generate migration AddDetailsToProducts part_number:string price:decimal
これにより、スキーママイグレーションが生成され、products
テーブルに2つの追加の列が追加されます。
class AddDetailsToProducts < ActiveRecord::Migration[7.1]
def change
add_column :products, :part_number, :string
add_column :products, :price, :decimal
end
end
2.3 列の削除
同様に、コマンドラインから列を削除するマイグレーションを生成することもできます。
$ bin/rails generate migration RemovePartNumberFromProducts part_number:string
これにより、適切なremove_column
ステートメントが生成されます。
class RemovePartNumberFromProducts < ActiveRecord::Migration[7.1]
def change
remove_column :products, :part_number, :string
end
end
2.4 新しいテーブルの作成
マイグレーション名が「CreateXXX」の形式であり、列名と型のリストが続く場合、テーブルXXXを作成し、 リストされた列を持つマイグレーションが生成されます。例えば:
$ bin/rails generate migration CreateProducts name:string part_number:string
これにより、次のマイグレーションが生成されます。
class CreateProducts < ActiveRecord::Migration[7.1]
def change
create_table :products do |t|
t.string :name
t.string :part_number
t.timestamps
end
end
end
いつものように、生成されたものは出発点に過ぎません。
db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb
ファイルを編集して、必要に応じて追加または削除することができます。
2.5 参照を使用した関連の作成
また、ジェネレータは列の型としてreferences
(またはbelongs_to
としても利用可能)を受け入れます。例えば、
$ bin/rails generate migration AddUserRefToProducts user:references
次のadd_reference
呼び出しが生成されます。
class AddUserRefToProducts < ActiveRecord::Migration[7.1]
def change
add_reference :products, :user, foreign_key: true
end
end
このマイグレーションはuser_id
列を作成します。参照は、列、インデックス、外部キー、または多態性の関連列を作成するための省略形です。
また、JoinTable
が名前の一部である場合、ジョインテーブルを生成するジェネレータもあります。
$ bin/rails generate migration CreateJoinTableCustomerProduct customer product
次のマイグレーションが生成されます。
class CreateJoinTableCustomerProduct < ActiveRecord::Migration[7.1]
def change
create_join_table :customers, :products do |t|
# t.index [:customer_id, :product_id]
# t.index [:product_id, :customer_id]
end
end
end
2.6 モデルジェネレータ
モデル、リソース、およびスキャフォールドジェネレータは、新しいモデルを追加するために適切なマイグレーションを作成します。 このマイグレーションには、関連するテーブルを作成するための手順も含まれています。欲しい列をRailsに伝えると、 これらの列を追加するためのステートメントも作成されます。例えば、次のコマンドを実行すると:
$ bin/rails generate model Product name:string description:text
次のようなマイグレーションが作成されます。
class CreateProducts < ActiveRecord::Migration[7.1]
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
列名/型のペアを追加することができます。
2.7 修飾子の指定
一部のよく使用されるtype modifiersは、コマンドラインで直接指定することができます。これらは波括弧で囲まれ、フィールドの型に続きます。
例えば、次のコマンドを実行すると:
$ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic}
次のようなマイグレーションが作成されます。
class AddDetailsToProducts < ActiveRecord::Migration[7.1]
def change
add_column :products, :price, :decimal, precision: 5, scale: 2
add_reference :products, :supplier, polymorphic: true
end
end
ジェネレータのヘルプ出力(bin/rails generate --help
)を参照して、詳細を確認してください。
3 マイグレーションの作成
ジェネレータのいずれかを使用してマイグレーションを作成したら、作業を開始する時が来ました!
3.1 テーブルの作成
create_table
メソッドは、最も基本的なメソッドの1つですが、ほとんどの場合、
モデル、リソース、またはスキャフォールドジェネレータを使用して生成されます。典型的な使用方法は、
create_table :products do |t|
t.string :name
end
このメソッドは、name
というカラムを持つproducts
テーブルを作成します。
デフォルトでは、create_table
は暗黙的にid
という主キーを作成します。
primary_key
オプションを使用してカラムの名前を変更することもできます。
または、主キーを使用しない場合は、id: false
オプションを渡すこともできます。
データベース固有のオプションを渡す必要がある場合は、options
オプションにSQLフラグメントを配置することができます。例えば:
create_table :products, options: "ENGINE=BLACKHOLE" do |t|
t.string :name, null: false
end
これにより、テーブルを作成するために使用されるSQLステートメントにENGINE=BLACKHOLE
が追加されます。
create_table
ブロック内で作成されたカラムには、index: true
またはオプションハッシュをindex
オプションに渡すことでインデックスを作成することができます。
create_table :users do |t|
t.string :name, index: true
t.string :email, index: { unique: true, name: 'unique_emails' }
end
また、comment
オプションを使用して、データベース自体に保存され、MySQL WorkbenchやPgAdmin IIIなどのデータベース管理ツールで表示できるテーブルの説明を指定することもできます。大規模なデータベースを持つアプリケーションでは、データモデルを理解し、ドキュメントを生成するのに役立つため、マイグレーションでコメントを指定することを強くお勧めします。現在、MySQLとPostgreSQLのアダプタのみがコメントをサポートしています。
3.2 結合テーブルの作成
マイグレーションメソッドcreate_join_table
は、HABTM(has and belongs to many)結合テーブルを作成します。典型的な使用例は次のとおりです。
create_join_table :products, :categories
このマイグレーションは、categories_products
という2つのカラムcategory_id
とproduct_id
を持つテーブルを作成します。
これらのカラムは、デフォルトでnull
オプションがfalse
に設定されているため、このテーブルにレコードを保存するためには値を指定する必要があります。column_options
オプションを指定することで、これを上書きすることができます。
create_join_table :products, :categories, column_options: { null: true }
デフォルトでは、create_join_table
の最初の2つの引数の結合で結合テーブルの名前が決まります。
テーブルの名前をカスタマイズするには、table_name
オプションを指定します。
create_join_table :products, :categories, table_name: :categorization
これにより、結合テーブルの名前が要求されたようにcategorization
になります。
また、create_join_table
はブロックを受け入れることができます。このブロックを使用して、デフォルトでは作成されないインデックスや任意の追加のカラムを追加することができます。
create_join_table :products, :categories do |t|
t.index :product_id
t.index :category_id
end
3.3 テーブルの変更
既存のテーブルを変更する場合は、change_table
を使用します。
これはcreate_table
と同様の方法で使用されますが、ブロック内で利用可能な特殊な関数にアクセスできます。例えば:
change_table :products do |t|
t.remove :description, :name
t.string :part_number
t.index :part_number
t.rename :upccode, :upc_code
end
このマイグレーションでは、description
とname
のカラムを削除し、part_number
という新しい文字列カラムを作成し、それにインデックスを追加します。最後に、upccode
カラムをupc_code
に名前変更します。
3.4 カラムの変更
remove_column
メソッドとadd_column
メソッドと同様に、Railsはchange_column
マイグレーションメソッドも提供しています。
change_column :products, :part_number, :text
これにより、products
テーブルのpart_number
カラムがtext
フィールドに変更されます。
注意:change_column
コマンドは元に戻せないです。
元に戻せるマイグレーションを自分で提供する必要があります。前述のように。
change_column
の他にも、change_column_null
とchange_column_default
メソッドが、カラムのnull
制約とデフォルト値を変更するために特に使用されます。
change_column_null :products, :name, false
change_column_default :products, :approved, from: true, to: false
これにより、products
のname
フィールドがNOT NULL
カラムに設定され、approved
フィールドのデフォルト値がtrueからfalseに変更されます。これらの変更は将来のトランザクションにのみ適用され、既存のレコードには適用されません。
null制約をtrueに設定すると、カラムはnull値を受け入れることを意味します。それ以外の場合は、NOT NULL
制約が適用され、レコードをデータベースに永続化するために値を渡す必要があります。
注意:上記のchange_column_default
マイグレーションをchange_column_default :products, :approved, false
と書くこともできますが、前の例とは異なり、この方法ではマイグレーションを元に戻せなくなります。
3.5 カラム修飾子
カラムを作成または変更する際に、カラム修飾子を適用することができます。
comment
:カラムにコメントを追加します。collation
:string
またはtext
カラムの照合順序を指定します。default
:カラムにデフォルト値を設定します。動的な値(日付など)を使用している場合は、デフォルト値は最初の一度だけ計算されます(つまり、マイグレーションが適用された日に計算されます)。NULL
の場合はnil
を使用します。limit
:string
カラムの最大文字数、およびtext/binary/integer
カラムの最大バイト数を設定します。null
:カラムでNULL
値を許可または拒否します。precision
:decimal/numeric/datetime/time
カラムの精度を指定します。scale
:decimal
およびnumeric
カラムのスケールを指定します。これは小数点以下の桁数を表します。 注意:add_column
またはchange_column
にはインデックスを追加するオプションはありません。 インデックスはadd_index
を使用して別途追加する必要があります。
一部のアダプタは追加のオプションをサポートしている場合があります。詳細については、アダプタ固有のAPIドキュメントを参照してください。
注意:マイグレーションを生成する際には、null
とdefault
はコマンドラインで指定できません。
3.6 参照
add_reference
メソッドを使用すると、1つ以上の関連付けの間の接続として適切に名前付けられた列を作成できます。
add_reference :users, :role
このマイグレーションは、usersテーブルにrole_id
列を作成します。index: false
オプションで明示的に指定しない限り、この列に対してもインデックスが作成されます。
詳細については、Active Record Associationsガイドを参照してください。
add_belongs_to
メソッドはadd_reference
のエイリアスです。
add_belongs_to :taggings, :taggable, polymorphic: true
polymorphicオプションにより、taggingsテーブルにポリモーフィック関連付けに使用できる2つの列、taggable_type
とtaggable_id
が作成されます。
polymorphic associationsについて詳しくは、このガイドを参照してください。
foreign_key
オプションを使用して外部キーを作成できます。
add_reference :users, :role, foreign_key: true
add_reference
のその他のオプションについては、APIドキュメントを参照してください。
参照は次のように削除することもできます。
remove_reference :products, :user, foreign_key: true, index: false
3.7 外部キー
必須ではありませんが、参照整合性を保証するために外部キー制約を追加することがあります。
add_foreign_key :articles, :authors
このadd_foreign_key
呼び出しは、articles
テーブルに新しい制約を追加します。この制約は、articles.author_id
が一致するauthors
テーブルの行が存在することを保証します。
from_table
の列名がto_table
の名前から派生できない場合は、column
オプションを使用できます。参照される主キーがid
でない場合は、primary_key
オプションを使用します。
例えば、articles.reviewer
がauthors.email
を参照する外部キーを追加する場合:
add_foreign_key :articles, :authors, column: :reviewer, primary_key: :email
これにより、articles
テーブルに、articles.reviewer
フィールドと一致するauthors
テーブルのemail
列が存在することを保証する制約が追加されます。
add_foreign_key
では、name
、on_delete
、if_not_exists
、validate
、deferrable
などの他のオプションもサポートされています。
remove_foreign_key
を使用して外部キーを削除することもできます。
# Active Recordに列名を推測させる
remove_foreign_key :accounts, :branches
# 特定の列の外部キーを削除する
remove_foreign_key :accounts, column: :owner_id
注意:Active Recordは単一列の外部キーのみをサポートしています。複合外部キーを使用するには、execute
およびstructure.sql
が必要です。Schema Dumping and Youを参照してください。
3.8 ヘルパーだけでは十分でない場合
Active Recordが提供するヘルパーが十分でない場合は、execute
メソッドを使用して任意のSQLを実行できます。
Product.connection.execute("UPDATE products SET price = 'free' WHERE 1=1")
個々のメソッドの詳細と例については、APIドキュメントを確認してください。
特に、ActiveRecord::ConnectionAdapters::SchemaStatements
のドキュメントでは、change
、up
、down
メソッドで使用できるメソッドが提供されています。
create_table
で生成されるオブジェクトに関するメソッドについては、ActiveRecord::ConnectionAdapters::TableDefinition
を参照してください。
change_table
で生成されるオブジェクトについては、ActiveRecord::ConnectionAdapters::Table
を参照してください。
3.9 change
メソッドの使用
change
メソッドは、マイグレーションを記述する主要な方法です。Active Recordがマイグレーションのアクションを自動的に逆向きにする方法を知っている場合、ほとんどの場合に使用できます。以下に、change
がサポートするいくつかのアクションを示します。
add_check_constraint
add_column
add_foreign_key
add_index
add_reference
add_timestamps
change_column_comment
(:from
と:to
オプションを指定する必要があります)change_column_default
(:from
と:to
オプションを指定する必要があります)change_column_null
change_table_comment
(:from
と:to
オプションを指定する必要があります)create_join_table
create_table
disable_extension
drop_join_table
drop_table
(ブロックを指定する必要があります)enable_extension
remove_check_constraint
(制約式を指定する必要があります)remove_column
(型を指定する必要があります)remove_columns
(:type
オプションを指定する必要があります)remove_foreign_key
(2番目のテーブルを指定する必要があります)remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table
change_table
も逆向きに実行できますが、ブロックが上記の操作のような逆向き操作のみを呼び出す場合です。
remove_column
は、3番目の引数として列の型を指定すれば逆向きに実行できます。元の列のオプションも指定してください。そうしないと、Railsはロールバック時に列を正確に再作成できません。
remove_column :posts, :slug, :string, null: false, default: ''
他のメソッドを使用する必要がある場合は、reversible
を使用するか、change
メソッドの代わりにup
メソッドとdown
メソッドを記述するかする必要があります。
3.10 reversible
の使用
複雑なマイグレーションでは、Active Recordが逆操作を知らない処理が必要になる場合があります。reversible
を使用して、マイグレーションを実行するときに何を行うか、および元に戻すときに何を行うかを指定できます。例えば:
class ExampleMigration < ActiveRecord::Migration[7.1]
def change
create_table :distributors do |t|
t.string :zipcode
end
reversible do |direction|
direction.up do
# ディストリビューターのビューを作成する
execute <<-SQL
CREATE VIEW distributors_view AS
SELECT id, zipcode
FROM distributors;
SQL
end
direction.down do
execute <<-SQL
DROP VIEW distributors_view;
SQL
end
end
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
end
reversible
を使用すると、指示が正しい順序で実行されることも保証されます。前の例のマイグレーションが元に戻されると、down
ブロックはhome_page_url
列が削除され、email_address
列が名前変更された後、distributors
テーブルが削除される直前に実行されます。
3.11 up
/down
メソッドの使用
change
メソッドの代わりにup
メソッドとdown
メソッドを使用して、古いスタイルのマイグレーションも行うことができます。
up
メソッドは、スキーマに加えたい変換を記述する必要があります。マイグレーションのdown
メソッドは、up
メソッドによって行われた変換を元に戻す必要があります。つまり、up
の後にdown
を実行してもデータベースのスキーマは変更されないはずです。
例えば、up
メソッドでテーブルを作成した場合、down
メソッドでテーブルを削除する必要があります。変換は、up
メソッドで行った順序とまったく逆の順序で行うことが賢明です。reversible
セクションの例は次のようになります:
class ExampleMigration < ActiveRecord::Migration[7.1]
def up
create_table :distributors do |t|
t.string :zipcode
end
# ディストリビューターのビューを作成する
execute <<-SQL
CREATE VIEW distributors_view AS
SELECT id, zipcode
FROM distributors;
SQL
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
execute <<-SQL
DROP VIEW distributors_view;
SQL
drop_table :distributors
end
end
3.12 元に戻せないようにエラーをスローする
マイグレーションが元に戻せない操作を行う場合、down
ブロックでActiveRecord::IrreversibleMigration
を発生させることができます。
マイグレーションを元に戻そうとすると、実行できないというエラーメッセージが表示されます。
3.13 前のマイグレーションを元に戻す
Active Recordのrevert
メソッドを使用して、マイグレーションをロールバックすることもできます。
require_relative "20121212123456_example_migration"
class FixupExampleMigration < ActiveRecord::Migration[7.1]
def change
revert ExampleMigration
create_table(:apples) do |t|
t.string :variety
end
end
end
revert
メソッドは、逆操作のブロックも受け入れます。これは、前のマイグレーションの一部を元に戻すために便利です。
例えば、ExampleMigration
がコミットされ、後でディストリビューターのビューが不要になったと判断された場合を想像してみましょう。
class DontUseDistributorsViewMigration < ActiveRecord::Migration[7.1]
def change
revert do
# ExampleMigrationからコピーしたコード
reversible do |direction|
direction.up do
# ディストリビューターのビューを作成する
execute <<-SQL
CREATE VIEW distributors_view AS
SELECT id, zipcode
FROM distributors;
SQL
end
direction.down do
execute <<-SQL
DROP VIEW distributors_view;
SQL
end
end
# マイグレーションの残りは問題ありません
end
end
end
同じマイグレーションは、revert
を使用せずに書くこともできますが、これにはさらにいくつかの手順が必要です:
create_table
とreversible
の順序を逆にする。create_table
をdrop_table
に置き換える。- 最後に、
up
をdown
に、down
をup
に置き換えます。
これはすべてrevert
によって処理されます。
4 マイグレーションの実行
Railsは、特定のセットのマイグレーションを実行するためのコマンドを提供しています。
おそらく最初に使用するマイグレーションに関連するRailsコマンドは、bin/rails db:migrate
でしょう。基本的な形式では、まだ実行されていないすべてのマイグレーションのchange
またはup
メソッドを実行します。そのようなマイグレーションが存在しない場合は、終了します。マイグレーションは、マイグレーションの日付に基づいて順番に実行されます。
db:migrate
コマンドを実行すると、db:schema:dump
コマンドも実行され、db/schema.rb
ファイルがデータベースの構造に合わせて更新されます。
ターゲットバージョンを指定すると、Active Recordは指定したバージョンに到達するまで必要なマイグレーション(change、up、down)を実行します。バージョンは、マイグレーションのファイル名の数値の接頭辞です。例えば、バージョン20080906120000にマイグレーションするには、次のコマンドを実行します:
bash
$ bin/rails db:migrate VERSION=20080906120000
バージョン20080906120000が現在のバージョンよりも大きい場合(つまり、上方向に移行している場合)、これは20080906120000を含むすべてのマイグレーションのchange
(またはup
)メソッドを実行し、それ以降のマイグレーションは実行しません。下方向に移行する場合、これは20080906120000を含まないすべてのマイグレーションのdown
メソッドを実行します。
4.1 ロールバック
よくあるタスクは、最後のマイグレーションをロールバックすることです。たとえば、それに間違いがあり修正したい場合、前のマイグレーションに関連付けられたバージョン番号を追跡する代わりに、次のコマンドを実行できます。
$ bin/rails db:rollback
これにより、最新のマイグレーションがロールバックされます。change
メソッドが元に戻されるか、down
メソッドが実行されます。複数のマイグレーションを元に戻す必要がある場合は、STEP
パラメータを指定できます。
$ bin/rails db:rollback STEP=3
最後の3つのマイグレーションが元に戻されます。
db:migrate:redo
コマンドは、ロールバックしてから再度マイグレーションを実行するためのショートカットです。db:rollback
コマンドと同様に、複数のバージョンを戻る必要がある場合はSTEP
パラメータを使用できます。例えば:
$ bin/rails db:migrate:redo STEP=3
これらのRailsコマンドは、db:migrate
で実行できないことは何もありません。バージョンを明示的に指定する必要がないため、便利なものです。
4.2 データベースのセットアップ
bin/rails db:setup
コマンドは、データベースを作成し、スキーマをロードし、シードデータで初期化します。
4.3 データベースのリセット
bin/rails db:reset
コマンドは、データベースを削除して再設定します。これはbin/rails db:drop db:setup
と機能的に同等です。
注意:これはすべてのマイグレーションを実行するのとは異なります。現在のdb/schema.rb
またはdb/structure.sql
ファイルの内容のみ使用します。マイグレーションをロールバックできない場合、bin/rails db:reset
は役に立たない場合があります。スキーマのダンプについて詳しくは、Schema Dumping and Youセクションを参照してください。
4.4 特定のマイグレーションの実行
特定のマイグレーションを上方向または下方向に実行する必要がある場合、db:migrate:up
およびdb:migrate:down
コマンドを使用できます。適切なバージョンを指定し、対応するマイグレーションのchange
、up
、またはdown
メソッドが呼び出されます。例:
$ bin/rails db:migrate:up VERSION=20080906120000
このコマンドを実行すると、バージョンが「20080906120000」のマイグレーションのchange
メソッド(またはup
メソッド)が実行されます。
まず、このコマンドはマイグレーションが存在し、既に実行されているかどうかをチェックし、そうであれば何もしません。
指定されたバージョンが存在しない場合、Railsは例外をスローします。
$ bin/rails db:migrate VERSION=zomg
rails aborted!
ActiveRecord::UnknownMigrationVersionError:
No migration with version number zomg.
4.5 異なる環境でのマイグレーションの実行
デフォルトでは、bin/rails db:migrate
はdevelopment
環境で実行されます。
他の環境でマイグレーションを実行するには、コマンドを実行する際にRAILS_ENV
環境変数を指定できます。たとえば、test
環境でマイグレーションを実行するには、次のように実行できます。
$ bin/rails db:migrate RAILS_ENV=test
4.6 マイグレーションの実行結果の変更
デフォルトでは、マイグレーションは実行内容と所要時間を正確に表示します。テーブルの作成とインデックスの追加を行うマイグレーションは、次のような出力を生成する場合があります。
== CreateProducts: migrating =================================================
-- create_table(:products)
-> 0.0028s
== CreateProducts: migrated (0.0028s) ========================================
マイグレーションでこれを制御するために、いくつかのメソッドが提供されています。
メソッド | 目的 |
---|---|
suppress_messages |
ブロックを引数として取り、ブロックによって生成される出力を抑制します。 |
say |
メッセージ引数を取り、そのまま出力します。2番目の真偽値引数を渡すことで、インデントするかどうかを指定できます。 |
say_with_time |
ブロックの実行にかかった時間とともにテキストを出力します。ブロックが整数を返す場合、それは影響を受けた行数と見なされます。 |
たとえば、次のマイグレーションを考えてみましょう。
class CreateProducts < ActiveRecord::Migration[7.1]
def change
suppress_messages do
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
say "Created a table"
suppress_messages { add_index :products, :name }
say "and an index!", true
say_with_time 'Waiting for a while' do
sleep 10
250
end
end
end
以下の出力が生成されます:
== CreateProducts: マイグレーション中 =================================================
-- テーブルが作成されました
-> インデックスも作成されました!
-- 少し待っています
-> 10.0013秒
-> 250行
== CreateProducts: マイグレーション完了 (10.0054秒) =======================================
Active Recordが何も出力しないようにしたい場合は、bin/rails db:migrate VERBOSE=false
を実行すると、すべての出力が抑制されます。
5 既存のマイグレーションの変更
マイグレーションを書く際に間違いを comit することがあります。もし既にマイグレーションを実行している場合、単にマイグレーションを編集して再度実行することはできません。Railsは既にマイグレーションを実行したと思っているため、bin/rails db:migrate
を実行しても何もしません。修正されたバージョンを実行するには、マイグレーションをロールバックする必要があります(たとえば、bin/rails db:rollback
を使用して)。その後、マイグレーションを編集し、修正版を実行するためにbin/rails db:migrate
を実行します。
一般的に、既存のマイグレーションを編集することは良いアイデアではありません。それによって、自分自身や同僚に余分な作業が生じ、既存のバージョンのマイグレーションが既に本番環境のマシンで実行されている場合には大きな問題が発生します。
代わりに、必要な変更を実行する新しいマイグレーションを作成するべきです。まだソースコントロールにコミットされていない(または一般的には、開発マシン以外には伝播していない)新しく生成されたマイグレーションを編集することは比較的無害です。
新しいマイグレーションを書く際に、以前のマイグレーションを全体または一部取り消すためにrevert
メソッドを使用することができます(詳細は前のマイグレーションの取り消しを参照してください)。
6 スキーマのダンプとあなた
6.1 スキーマファイルとは何ですか?
マイグレーションは強力ですが、データベースのスキーマの正確な情報源ではありません。データベースが真実の情報源です。
デフォルトでは、Railsは現在のデータベーススキーマの状態をキャプチャしようとするdb/schema.rb
を生成します。
bin/rails db:schema:load
を使用してスキーマファイルを読み込むことで、アプリケーションのデータベースの新しいインスタンスを作成する方が、マイグレーションの履歴全体を再生するよりも速く、エラーが少なくなります。
古いマイグレーションは、それらのマイグレーションが変化する外部依存関係を使用したり、マイグレーションとは別に進化するアプリケーションコードに依存したりする場合、正しく適用されない場合があります。
スキーマファイルは、Active Recordオブジェクトが持つ属性を素早く確認するためにも便利です。この情報はモデルのコードには含まれておらず、頻繁に複数のマイグレーションに分散していますが、スキーマファイルにはこれらの情報がまとめられています。
6.2 スキーマダンプの種類
Railsによって生成されるスキーマダンプの形式は、config/application.rb
で定義されたconfig.active_record.schema_format
設定によって制御されます。デフォルトでは、形式は:ruby
であり、代わりに:sql
に設定することもできます。
6.2.1 デフォルトの:ruby
スキーマの使用
:ruby
が選択されている場合、スキーマはdb/schema.rb
に保存されます。このファイルを見ると、非常に大きなマイグレーションのように見えることに気づくでしょう:
ActiveRecord::Schema[7.1].define(version: 2008_09_06_171750) do
create_table "authors", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", force: true do |t|
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "part_number"
end
end
多くの点で、これがまさにその通りです。このファイルは、データベースを調査し、create_table
、add_index
などを使用してその構造を表現することで作成されます。
6.2.2 :sql
スキーマダンパーの使用
ただし、db/schema.rb
ではトリガーやシーケンス、ストアドプロシージャなど、データベースがサポートする可能性のあるすべてのものを表現することはできません。
マイグレーションがRubyのマイグレーションDSLではサポートされていないデータベース構造を作成するためにexecute
を使用する場合、スキーマダンパーによって再構成することができない場合があります。
このような機能を使用している場合は、スキーマ形式を:sql
に設定して、新しいデータベースインスタンスを作成するために役立つ正確なスキーマファイルを取得する必要があります。
スキーマ形式が:sql
に設定されている場合、データベースの構造は、データベース固有のツールを使用してdb/structure.sql
にダンプされます。たとえば、PostgreSQLの場合はpg_dump
ユーティリティが使用されます。MySQLとMariaDBの場合、このファイルにはさまざまなテーブルのSHOW CREATE TABLE
の出力が含まれます。
db/structure.sql
からスキーマをロードするには、bin/rails db:schema:load
を実行します。このファイルの読み込みは、それが含むSQLステートメントを実行することによって行われます。定義によれば、これによりデータベースの構造の完全なコピーが作成されます。
6.3 スキーマダンプとソースコントロール
スキーマファイルは新しいデータベースを作成するために一般的に使用されるため、ソースコントロールにスキーマファイルをチェックインすることを強くお勧めします。
スキーマファイルでマージの競合が発生することがあります。これらの競合を解決するには、bin/rails db:migrate
を実行してスキーマファイルを再生成してください。
新しく生成されたRailsアプリには、マイグレーションフォルダが既にgitツリーに含まれているため、追加する新しいマイグレーションを追加してコミットするだけで済みます。
7 Active Recordと参照整合性
Active Recordの方法では、知識はデータベースではなくモデルに属するとされています。そのため、トリガーや制約など、一部の知識をデータベースに戻す機能は推奨されません。
validates :foreign_key, uniqueness: true
のようなバリデーションは、モデルがデータの整合性を強制する方法の一つです。関連付けのdependent
オプションは、親が削除されると自動的に子オブジェクトを削除することができます。アプリケーションレベルで動作するものと同様に、これらは参照整合性を保証することはできず、一部の人々はデータベースに外部キー制約を追加して補完します。
Active Recordは直接これらの機能を操作するためのすべてのツールを提供していませんが、execute
メソッドを使用して任意のSQLを実行することができます。
8 マイグレーションとシードデータ
Railsのマイグレーション機能の主な目的は、一貫したプロセスを使用してスキーマを変更するコマンドを発行することです。マイグレーションはデータを追加または変更するためにも使用することができます。これは、プロダクションデータベースなど、破棄して再作成できない既存のデータベースで特に有用です。
class AddInitialProducts < ActiveRecord::Migration[7.1]
def up
5.times do |i|
Product.create(name: "Product ##{i}", description: "A product.")
end
end
def down
Product.delete_all
end
end
データベースが作成された後に初期データを追加するには、Railsには組み込みの「シード」機能があり、プロセスを高速化します。これは、開発およびテスト環境でデータベースを頻繁にリロードする場合や、プロダクションの初期データを設定する場合に特に便利です。
この機能を使用するには、db/seeds.rb
を開き、いくつかのRubyコードを追加し、bin/rails db:seed
を実行します。
注意: ここでのコードは、どの環境でもいつでも実行できるように、冪等性を持つ必要があります。
["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
MovieGenre.find_or_create_by!(name: genre_name)
end
これは、空のアプリケーションのデータベースを設定するための一般的にはるかにクリーンな方法です。
9 古いマイグレーション
db/schema.rb
またはdb/structure.sql
は、データベースの現在の状態のスナップショットであり、そのデータベースを再構築するための権威あるソースです。これにより、古いマイグレーションファイルを削除または整理することができます。
db/migrate/
ディレクトリ内のマイグレーションファイルを削除すると、bin/rails db:migrate
がそれらのファイルがまだ存在していた時に実行された環境では、特定の環境内で実行されたマイグレーションタイムスタンプへの参照が、内部のRailsデータベーステーブルであるschema_migrations
に保持されます。このテーブルは、特定の環境でマイグレーションが実行されたかどうかを追跡するために使用されます。
bin/rails db:migrate:status
コマンドを実行すると、各マイグレーションのステータス(上または下)が表示されますが、db/migrate/
ディレクトリに存在しない削除されたマイグレーションファイルの横には********** NO FILE **********
と表示されるはずです。
9.1 エンジンからのマイグレーション
ただし、エンジンには注意点があります。エンジンからのマイグレーションをインストールするためのRakeタスクは冪等性を持ちます。つまり、何度呼び出しても同じ結果になります。以前のインストールにより親アプリケーションに存在するマイグレーションはスキップされ、欠落しているマイグレーションは新しいタイムスタンプでコピーされます。古いエンジンのマイグレーションを削除してインストールタスクを再実行すると、新しいタイムスタンプ付きの新しいファイルが作成され、db:migrate
が再度実行されます。
したがって、通常はエンジンからのマイグレーションを保持することが望ましいです。次のような特別なコメントがあります。
# This migration comes from blorgh (originally 20210621082949)
フィードバック
このガイドの品質向上にご協力ください。
タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。
不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。
修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください。
そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。