Rails Migrationsとは?データベース変更を管理する仕組み完全ガイド

プログラミング・IT

「Railsでデータベースにテーブルを追加したいけど、どうすればいい?」
「間違えてカラムを削除してしまった…元に戻せる?」

Ruby on Railsでアプリケーションを開発していると、データベースの構造を変更する必要が頻繁に出てきます。

Rails Migrations(レイルス・マイグレーション)は、データベースの変更を管理するための仕組みです。直接SQLを書かなくても、Rubyのコードでデータベースを変更でき、変更履歴も自動的に記録されます。この記事では、Rails Migrationsの基本から実践的な使い方まで、初心者の方にも分かりやすく解説します。

データベースの変更を、安全かつ効率的に管理しましょう。


スポンサーリンク
  1. Rails Migrationsとは何か
    1. マイグレーションの基本概念
    2. なぜマイグレーションが必要なのか
  2. マイグレーションの基本構造
    1. マイグレーションファイルの場所
    2. 基本的なマイグレーションの構造
  3. マイグレーションの作成方法
    1. ジェネレーターを使う
    2. データ型の指定
  4. マイグレーションの実行
    1. マイグレーションの実行コマンド
    2. 実行の流れ
  5. ロールバック(変更の取り消し)
    1. 基本的なロールバック
    2. ロールバックの注意点
  6. テーブル操作のマイグレーション
    1. テーブルの作成
    2. テーブルの削除
    3. テーブル名の変更
  7. カラム操作のマイグレーション
    1. カラムの追加
    2. カラムの削除
    3. カラム名の変更
    4. カラムのデータ型変更
    5. カラムのオプション変更
  8. インデックスの追加
    1. 単一カラムのインデックス
    2. 一意制約付きインデックス
    3. 複数カラムのインデックス
    4. インデックスの削除
  9. 外部キー制約
    1. 外部キーの追加
    2. リファレンスカラムの追加
  10. 高度なマイグレーション
    1. upとdownメソッドを使う
    2. データの変更
    3. 可逆的でないマイグレーション
  11. schema.rbの理解
    1. schema.rbとは
    2. schema.rbの例
    3. schema.rbの使い方
  12. よくあるパターンと実例
    1. ユーザーテーブルの作成
    2. 多対多の関連テーブル
    3. 既存カラムへのNOT NULL制約追加
  13. ベストプラクティス
    1. 1つのマイグレーションで1つのことをする
    2. マイグレーションは削除しない
    3. 本番環境での注意
    4. インデックスを忘れずに
  14. よくあるトラブルと解決方法
    1. マイグレーションが実行できない
    2. ロールバックできない
    3. マイグレーションの順序が狂った
    4. 本番環境でマイグレーションが失敗
  15. テストでのマイグレーション
    1. テストデータベースのセットアップ
    2. マイグレーションのテスト
  16. まとめ:マイグレーションでデータベースを安全に管理

Rails Migrationsとは何か

Rails Migrationsは、データベースのスキーマ(構造)を変更するための仕組みです。

マイグレーションの基本概念

マイグレーション(Migration)とは、英語で「移行」という意味です。

Railsでは、データベースの状態をある状態から別の状態へ「移行」させることを指します。例えば、「usersテーブルにemailカラムを追加する」といった変更が、1つのマイグレーションになります。

なぜマイグレーションが必要なのか

直接SQLでデータベースを変更することもできますが、マイグレーションを使うと以下のメリットがあります。

バージョン管理ができる

すべての変更が履歴として残り、いつでも過去の状態に戻せます。

チーム開発がしやすい

マイグレーションファイルをGitで共有すれば、チームメンバー全員が同じデータベース構造を持てます。

データベースに依存しない

Railsのコードで書くので、MySQL、PostgreSQL、SQLiteなど、どのデータベースでも同じマイグレーションファイルが使えます。

間違いを元に戻せる

ロールバック機能で、変更を取り消せます。


マイグレーションの基本構造

マイグレーションファイルがどんな構造になっているか見ていきましょう。

マイグレーションファイルの場所

マイグレーションファイルは、db/migrate/フォルダに保存されます。

ファイル名は以下のような形式です:

20250115123456_create_users.rb

最初の数字はタイムスタンプ(作成日時)で、マイグレーションの実行順序を決めます。

基本的なマイグレーションの構造

典型的なマイグレーションファイルはこんな感じです:

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.integer :age

      t.timestamps
    end
  end
end

クラス名

CreateUsersのように、何をするマイグレーションかが分かる名前を付けます。

changeメソッド

データベースに対して行う変更を記述します。Railsは自動的に、変更を元に戻す方法も理解してくれます。

create_table

新しいテーブルを作成する命令です。

t.timestamps

created_at(作成日時)とupdated_at(更新日時)の2つのカラムを自動的に追加します。


マイグレーションの作成方法

新しいマイグレーションを作成する方法を説明します。

ジェネレーターを使う

Railsには、マイグレーションファイルを自動生成する便利なコマンドがあります。

テーブルを作成するマイグレーション

rails generate migration CreateUsers name:string email:string age:integer

または短縮形:

rails g migration CreateUsers name:string email:string age:integer

これで、db/migrate/フォルダに新しいマイグレーションファイルが作成されます。

カラムを追加するマイグレーション

既存のテーブルにカラムを追加する場合:

rails g migration AddPhoneToUsers phone:string

AddXxxToYyyという命名規則を使うと、Railsが自動的に適切なコードを生成してくれます。

カラムを削除するマイグレーション

rails g migration RemovePhoneFromUsers phone:string

RemoveXxxFromYyyという命名規則で、カラム削除のコードが生成されます。

データ型の指定

カラムを作成する時、様々なデータ型を指定できます。

主なデータ型

  • string:短い文字列(255文字まで)
  • text:長い文字列
  • integer:整数
  • bigint:大きな整数
  • float:浮動小数点数
  • decimal:正確な小数(金額など)
  • boolean:真偽値(true/false)
  • date:日付
  • datetime:日時
  • time:時刻
  • binary:バイナリデータ
  • json:JSON形式のデータ
  • jsonb:JSON形式のデータ(PostgreSQL)

マイグレーションの実行

作成したマイグレーションを実行して、実際にデータベースを変更します。

マイグレーションの実行コマンド

すべての未実行マイグレーションを実行

rails db:migrate

このコマンドで、まだ実行されていないマイグレーションがすべて実行されます。

実行状況の確認

rails db:migrate:status

どのマイグレーションが実行済みで、どれが未実行かを確認できます。

実行の流れ

マイグレーションを実行すると、以下のことが起こります:

  1. db/migrate/フォルダ内のマイグレーションファイルを確認
  2. まだ実行されていないものを、古い順に実行
  3. 実行済みのマイグレーションをschema_migrationsテーブルに記録

ロールバック(変更の取り消し)

間違えたり、やり直したい時は、ロールバックで元に戻せます。

基本的なロールバック

直前のマイグレーションを取り消す

rails db:rollback

最後に実行したマイグレーションが取り消されます。

複数のマイグレーションを取り消す

rails db:rollback STEP=3

STEP=3で、直近3つのマイグレーションを取り消します。

特定のバージョンまで戻る

rails db:migrate:down VERSION=20250115123456

特定のバージョン番号のマイグレーションだけを取り消します。

ロールバックの注意点

ロールバックは、データを失う可能性があります。

例えば、カラムを削除するマイグレーションをロールバックしても、削除されたデータは復活しません。本番環境では特に注意が必要です。


テーブル操作のマイグレーション

テーブルに関する様々な操作を見ていきましょう。

テーブルの作成

class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :name, null: false
      t.text :description
      t.decimal :price, precision: 10, scale: 2
      t.integer :stock, default: 0
      t.boolean :available, default: true

      t.timestamps
    end
  end
end

オプションの説明

  • null: false:NULL値を許可しない(必須項目)
  • default: 0:デフォルト値を設定
  • precision: 10, scale: 2:小数の桁数を指定(10桁中、小数点以下2桁)

テーブルの削除

class DropProducts < ActiveRecord::Migration[7.0]
  def change
    drop_table :products
  end
end

注意:drop_tableはロールバックできません。データも完全に失われます。

テーブル名の変更

class RenameProductsToItems < ActiveRecord::Migration[7.0]
  def change
    rename_table :products, :items
  end
end

カラム操作のマイグレーション

カラムの追加、削除、変更の方法を説明します。

カラムの追加

class AddCategoryToProducts < ActiveRecord::Migration[7.0]
  def change
    add_column :products, :category, :string
    add_column :products, :published_at, :datetime
  end
end

カラムの削除

class RemoveCategoryFromProducts < ActiveRecord::Migration[7.0]
  def change
    remove_column :products, :category, :string
  end
end

注意:カラムを削除すると、そのカラムのデータも失われます。

カラム名の変更

class RenameProductNameToTitle < ActiveRecord::Migration[7.0]
  def change
    rename_column :products, :name, :title
  end
end

カラムのデータ型変更

class ChangeProductPriceType < ActiveRecord::Migration[7.0]
  def change
    change_column :products, :price, :integer
  end
end

注意:データ型を変更すると、既存のデータに影響が出る可能性があります。

カラムのオプション変更

class ChangeProductsColumns < ActiveRecord::Migration[7.0]
  def change
    change_column_null :products, :name, false  # NOT NULL制約を追加
    change_column_default :products, :stock, from: 0, to: 1  # デフォルト値を変更
  end
end

インデックスの追加

インデックスを追加すると、データの検索が速くなります。

単一カラムのインデックス

class AddIndexToUsersEmail < ActiveRecord::Migration[7.0]
  def change
    add_index :users, :email
  end
end

一意制約付きインデックス

class AddUniqueIndexToUsersEmail < ActiveRecord::Migration[7.0]
  def change
    add_index :users, :email, unique: true
  end
end

unique: trueで、同じ値が重複して登録されるのを防ぎます。

複数カラムのインデックス

class AddIndexToOrders < ActiveRecord::Migration[7.0]
  def change
    add_index :orders, [:user_id, :created_at]
  end
end

インデックスの削除

class RemoveIndexFromUsersEmail < ActiveRecord::Migration[7.0]
  def change
    remove_index :users, :email
  end
end

外部キー制約

テーブル間の関連を強制する外部キー制約を追加できます。

外部キーの追加

class AddForeignKeyToOrders < ActiveRecord::Migration[7.0]
  def change
    add_foreign_key :orders, :users
  end
end

これで、ordersテーブルのuser_idが、usersテーブルのidを参照するようになります。

リファレンスカラムの追加

外部キーとインデックスを同時に追加する便利な方法:

class AddUserToOrders < ActiveRecord::Migration[7.0]
  def change
    add_reference :orders, :user, foreign_key: true, index: true
  end
end

これで、user_idカラム、インデックス、外部キー制約がすべて追加されます。


高度なマイグレーション

より複雑な操作が必要な場合の方法を紹介します。

upとdownメソッドを使う

changeメソッドで自動的にロールバックできない場合は、updownを明示的に定義します。

class AddCheckConstraintToProducts < ActiveRecord::Migration[7.0]
  def up
    execute <<-SQL
      ALTER TABLE products
      ADD CONSTRAINT check_price_positive
      CHECK (price > 0)
    SQL
  end

  def down
    execute <<-SQL
      ALTER TABLE products
      DROP CONSTRAINT check_price_positive
    SQL
  end
end

データの変更

マイグレーションでデータを変更することもできます(ただし推奨されません)。

class UpdateExistingUserRoles < ActiveRecord::Migration[7.0]
  def up
    User.where(role: nil).update_all(role: 'member')
  end

  def down
    # ロールバック時の処理
  end
end

注意:大量のデータを扱う場合は、マイグレーションではなく、Rakeタスクを使う方が安全です。

可逆的でないマイグレーション

一部のマイグレーションは、自動的にロールバックできません。

class RemoveOldColumn < ActiveRecord::Migration[7.0]
  def change
    remove_column :users, :old_field
  end
end

このような場合、ロールバック時にエラーが出ます。元に戻す必要がある場合は、updownを使います。


schema.rbの理解

マイグレーションを実行すると、db/schema.rbが自動的に更新されます。

schema.rbとは

schema.rbは、現在のデータベース構造の全体像を示すファイルです。

すべてのマイグレーションの結果を1つのファイルにまとめたものです。

schema.rbの例

ActiveRecord::Schema[7.0].define(version: 2025_01_15_123456) do
  create_table "users", force: :cascade do |t|
    t.string "name", null: false
    t.string "email", null: false
    t.integer "age"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
  end
end

schema.rbの使い方

新しい環境でデータベースを構築

rails db:schema:load

このコマンドで、schema.rbを元にデータベースを一気に構築できます。すべてのマイグレーションを1つずつ実行するより高速です。


よくあるパターンと実例

実際の開発でよく使うマイグレーションのパターンを紹介します。

ユーザーテーブルの作成

class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.string :password_digest
      t.boolean :admin, default: false

      t.timestamps
    end

    add_index :users, :email, unique: true
  end
end

多対多の関連テーブル

class CreateUserGroups < ActiveRecord::Migration[7.0]
  def change
    create_table :user_groups do |t|
      t.references :user, null: false, foreign_key: true
      t.references :group, null: false, foreign_key: true

      t.timestamps
    end

    add_index :user_groups, [:user_id, :group_id], unique: true
  end
end

既存カラムへのNOT NULL制約追加

class AddNotNullToUserName < ActiveRecord::Migration[7.0]
  def change
    # まず、NULL値を持つレコードを更新
    User.where(name: nil).update_all(name: 'Unknown')

    # その後、NOT NULL制約を追加
    change_column_null :users, :name, false
  end
end

ベストプラクティス

マイグレーションを書く時の推奨される方法を紹介します。

1つのマイグレーションで1つのことをする

マイグレーションは小さく、明確にしましょう。

悪い例

class UpdateEverything < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :phone, :string
    remove_column :products, :old_field
    create_table :categories do |t|
      # ...
    end
  end
end

良い例

別々のマイグレーションに分ける:

  • AddPhoneToUsers
  • RemoveOldFieldFromProducts
  • CreateCategories

マイグレーションは削除しない

一度コミットしたマイグレーションは削除せず、新しいマイグレーションで修正します。

特に、他の開発者が既に実行している場合は絶対に削除してはいけません。

本番環境での注意

本番環境でマイグレーションを実行する前に:

  1. データのバックアップを取る
  2. ステージング環境で先にテストする
  3. ダウンタイム(サービス停止時間)を考慮する
  4. 大量データがある場合は、処理時間を見積もる

インデックスを忘れずに

外部キーカラムには、必ずインデックスを追加しましょう。

class AddUserToOrders < ActiveRecord::Migration[7.0]
  def change
    add_reference :orders, :user, foreign_key: true, index: true
  end
end

よくあるトラブルと解決方法

マイグレーションで困った時の対処法を紹介します。

マイグレーションが実行できない

症状

rails db:migrateを実行してもエラーが出る。

原因と解決方法

構文エラー

マイグレーションファイルのRubyコードに間違いがあります。エラーメッセージを読んで、該当箇所を修正しましょう。

データベース接続エラー

database.ymlの設定を確認します。データベースサーバーが起動しているかも確認しましょう。

既にカラムが存在する

既に存在するカラムを追加しようとしている場合は、マイグレーションをロールバックしてから修正します。

ロールバックできない

症状

rails db:rollbackを実行してもエラーが出る。

解決方法

updownメソッドを明示的に定義します。

class MyMigration < ActiveRecord::Migration[7.0]
  def up
    # マイグレーション実行時の処理
  end

  def down
    # ロールバック時の処理
  end
end

マイグレーションの順序が狂った

症状

マイグレーションが期待した順序で実行されない。

原因

タイムスタンプが正しくない可能性があります。

解決方法

ファイル名の先頭にあるタイムスタンプを確認し、必要に応じて手動で調整します。ただし、これは最終手段です。

本番環境でマイグレーションが失敗

症状

本番環境でマイグレーションを実行したら、データが壊れた。

対処法

  1. すぐにサービスを停止
  2. バックアップからデータベースを復元
  3. 問題を修正してから再度実行

予防策

  • 本番環境での実行前に、必ずバックアップを取る
  • ステージング環境で先にテストする
  • できれば、メンテナンス時間を設けて実行する

テストでのマイグレーション

テスト環境でマイグレーションを扱う方法です。

テストデータベースのセットアップ

rails db:test:prepare

このコマンドで、テスト用データベースがschema.rbを元に構築されます。

マイグレーションのテスト

マイグレーション自体をテストすることもできます。

require 'test_helper'

class CreateUsersTest < ActiveSupport::TestCase
  def setup
    @migration = CreateUsers.new
  end

  test "creates users table" do
    @migration.migrate(:up)
    assert ActiveRecord::Base.connection.table_exists?(:users)
  end
end

まとめ:マイグレーションでデータベースを安全に管理

Rails Migrationsは、データベースの変更を安全かつ効率的に管理する強力な仕組みです。

この記事のポイント

  • マイグレーションはデータベース変更の履歴管理システム
  • rails g migrationでマイグレーションファイルを作成
  • rails db:migrateで実行、rails db:rollbackで取り消し
  • テーブル、カラム、インデックス、外部キーを管理できる
  • 1つのマイグレーションで1つのことをする
  • 本番環境では慎重に実行し、必ずバックアップを取る
  • schema.rbで現在の構造を確認できる

マイグレーションを使えば、データベースの変更をコードとして管理でき、チーム開発もスムーズになります。最初は戸惑うかもしれませんが、慣れればとても便利な機能です。

Rails開発では、マイグレーションはほぼ毎日使う基本的なツールです。この記事を参考に、安全にデータベースを変更する方法を身につけてください。間違えてもロールバックで戻せるので、恐れずに試してみましょう。

コメント

タイトルとURLをコピーしました