edge
詳細はrubyonrails.orgで: もっとRuby on Rails

Active Record Migrations

マイグレーションは、Active Recordの機能であり、データベーススキーマを時間とともに進化させることができます。純粋なSQLではなく、RubyのDSLを使用してテーブルの変更を記述することができます。

このガイドを読むことで、以下のことがわかります。

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_atupdated_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.updirection.downに渡されるブロックに注目してください。

または、changeの代わりにupdownを使用することもできます。

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.rbCreateProductsクラスを定義し、20080906120001_add_details_to_products.rbAddDetailsToProductsを定義する必要があります。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_idproduct_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

このマイグレーションでは、descriptionnameのカラムを削除し、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_nullchange_column_defaultメソッドが、カラムのnull制約とデフォルト値を変更するために特に使用されます。

change_column_null :products, :name, false
change_column_default :products, :approved, from: true, to: false

これにより、productsnameフィールドがNOT NULLカラムに設定され、approvedフィールドのデフォルト値がtrueからfalseに変更されます。これらの変更は将来のトランザクションにのみ適用され、既存のレコードには適用されません。

null制約をtrueに設定すると、カラムはnull値を受け入れることを意味します。それ以外の場合は、NOT NULL制約が適用され、レコードをデータベースに永続化するために値を渡す必要があります。

注意:上記のchange_column_defaultマイグレーションをchange_column_default :products, :approved, falseと書くこともできますが、前の例とは異なり、この方法ではマイグレーションを元に戻せなくなります。

3.5 カラム修飾子

カラムを作成または変更する際に、カラム修飾子を適用することができます。

  • comment:カラムにコメントを追加します。
  • collationstringまたはtextカラムの照合順序を指定します。
  • default:カラムにデフォルト値を設定します。動的な値(日付など)を使用している場合は、デフォルト値は最初の一度だけ計算されます(つまり、マイグレーションが適用された日に計算されます)。NULLの場合はnilを使用します。
  • limitstringカラムの最大文字数、およびtext/binary/integerカラムの最大バイト数を設定します。
  • null:カラムでNULL値を許可または拒否します。
  • precisiondecimal/numeric/datetime/timeカラムの精度を指定します。
  • scaledecimalおよびnumericカラムのスケールを指定します。これは小数点以下の桁数を表します。 注意:add_columnまたはchange_columnにはインデックスを追加するオプションはありません。 インデックスはadd_indexを使用して別途追加する必要があります。

一部のアダプタは追加のオプションをサポートしている場合があります。詳細については、アダプタ固有のAPIドキュメントを参照してください。

注意:マイグレーションを生成する際には、nulldefaultはコマンドラインで指定できません。

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_typetaggable_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.reviewerauthors.emailを参照する外部キーを追加する場合:

add_foreign_key :articles, :authors, column: :reviewer, primary_key: :email

これにより、articlesテーブルに、articles.reviewerフィールドと一致するauthorsテーブルのemail列が存在することを保証する制約が追加されます。

add_foreign_keyでは、nameon_deleteif_not_existsvalidatedeferrableなどの他のオプションもサポートされています。

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のドキュメントでは、changeupdownメソッドで使用できるメソッドが提供されています。

create_tableで生成されるオブジェクトに関するメソッドについては、ActiveRecord::ConnectionAdapters::TableDefinitionを参照してください。

change_tableで生成されるオブジェクトについては、ActiveRecord::ConnectionAdapters::Tableを参照してください。

3.9 changeメソッドの使用

changeメソッドは、マイグレーションを記述する主要な方法です。Active Recordがマイグレーションのアクションを自動的に逆向きにする方法を知っている場合、ほとんどの場合に使用できます。以下に、changeがサポートするいくつかのアクションを示します。

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を使用せずに書くこともできますが、これにはさらにいくつかの手順が必要です:

  1. create_tablereversibleの順序を逆にする。
  2. create_tabledrop_tableに置き換える。
  3. 最後に、updownに、downupに置き換えます。

これはすべて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コマンドを使用できます。適切なバージョンを指定し、対応するマイグレーションのchangeup、または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:migratedevelopment環境で実行されます。

他の環境でマイグレーションを実行するには、コマンドを実行する際に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_tableadd_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フォーラムで大歓迎です。