RailsのMigrationに関する基本まとめ

Rails初心者でmigration操作について悩む人（自分）のための忘備録

# とりあえず基本の確認
データベースを変更するときの大まかな流れは以下。

1.`$ rails g migration`コマンドでマイグレーションファイルを作成
2.マイグレーションファイルの中身を書き換える
3.`$ rails db:migrate`コマンドでマイグレーションを順番に実行してデータベースに変更を加える

今回サンプルとして以下のようなデータ構造を持つUserテーブルを作成する↓

| カラム名       |       データ型 |
|:-----------------|------------------:|
| id             |              integer    |
| name           |           string       |
| profile             |              text |
| created_at               |                datetime |
| updated_at             |             datetime |


### 1. 空のマイグレーションファイルを作成する

とりあえず`rails g migration`コマンドを打ってみる↓

```bash
# 空のmigrationファイルを作成する
$ rails g migration create_user 
Running via Spring preloader in process 2198
      invoke  active_record
      create    db/migrate/20181219035049_create_user.rb # 20181219035049というのがmigrationファイルが作られた日時

# マイグレーションの実行状況を確かめる
$ rails db:migrate:status

database: hogehoge/your_project

 Status   Migration ID    Migration Name
--------------------------------------------------
 down     20181219035049  CreateUser # statusが'down'となっているのはマイグレーションがまだ実行されていないことを示している
```

db/migrate配下に、現在時刻を記録したmigrationファイルが作成された↓

```rb
# db/migrate/20181219035049_create_user.rbの中身
class CreateUser < ActiveRecord::Migration[5.2]
  def change
  end
end
```

### ２. データベースに変更を加える

当たり前だがマイグレーションファイルを作成しただけでは、データベースは変更されない。


先ほど作成したマイグレーションファイルの中身を書き換えて、`rails db:migrate`を打てば完了。
**`rails db:migrate`によって`change`メソッドと`up`メソッドが順番に実行される。**

```rb
# db/migrate/20181219035049_create_user.rbを書き換える
class CreateUser < ActiveRecord::Migration[5.2]
  def change # rails db:migrateで実行されるメソッド
    create_table :users do |t| #usersという名前のテーブルを新たに作成
      #(idカラムは自動で割り当てられる)
      t.string :name #string型のnameカラムを作成
      t.text :profile #text型のprofileカラムを作成
      t.timestamps #created_at, updated_atを自動で作成
    end
  end
end
```

```bash
#マイグレーションファイルを実行してデータベースに変更を加える
$ rails db:migrate
== 20181219035118 CreateUsers: migrating ======================================
-- create_table(:user)
   -> 0.0024s
== 20181219035118 CreateUsers: migrated (0.0026s) =============================

# マイグレーションの実行状況を確かめる
$ rails db:migrate:status

database: hogehoge/your_project

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20181219035049  CreateUser # statusが'up'に変わり、マイグレーションが正常に実行されたことを示している
```

### 3. データベースへの変更を取り消す
 

`rails db:rollback`コマンドによって、直前の変更(migrationファイル1つ分)を取り消すことができる。
**`rails db:rollback`によって`change`メソッドに定義された処理が逆順に実行される。**
※`down`メソッドがあればそれも順番に実行していく。

```bash
# 直前のマイグレーションの取り消し
$ rails db:rollback
== 20181219036118 CreateUsers: reverting ======================================
-- drop_table(:users) #usersテーブルを削除
   -> 0.0014s
== 20181219036118 CreateUsers: reverted (0.0034s) =============================

# マイグレーションの実行状況を確認
$ rails db:migrate:status

database: hogehoge/your_project

 Status   Migration ID    Migration Name
--------------------------------------------------
 down     20181219035049  CreateUser
```

この状況でマイグレーションファイルを削除すれば変更完了。


# `change_column`はロールバックできない

```rb:Migrationファイル

class ChangeColumnToUser < ActiveRecord::Migration[5.2]
  def change
    change_column :users, :name, :text
  end
end
```


```bash:terminal
# db:migrateは実行できる
$ rails db:migrate
== 20181219044049 ChangeColumnToUser: migrating =======================
-- change_column(:users, :name, :text)
   -> 0.1817s
== 20181219044049 ChangeColumnToUser: migrated (0.1819s) ==============

# db:rollbackはできない
$ rails db:rollback
== 20181219045049 ChangeColumnToUser: reverting ============================
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

ActiveRecord::IrreversibleMigration # マイグレーションが可逆でないことを示すエラー
```

`change`メソッドの中で`change_column`を使おうとすると、マイグレーションが可逆的でないというエラーが発生する。
これは`change`メソッドが行える処理の中に、`change_column`が定義されていないからである。
公式ドキュメントによると`change`メソッドは以下の処理しか実行できない↓
>現時点では、changeでサポートされているマイグレーション定義は以下のものだけです。

>add_column
add_foreign_key
add_index
add_reference
add_timestamps
change_column_default (:fromと:toの指定は省略できない)
change_column_null
create_join_table
create_table
disable_extension
drop_join_table
drop_table （ブロックを渡さなければならない）
enable_extension
remove_column（型を指定しなければならない）
remove_foreign_key（2番目のテーブルを指定しなければならない）
remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table

>--- Rails公式ドキュメントより引用 ---


`change_column`を使いたい場合、`up`,`down`メソッドを定義してあげると良い。

```rb:migration
class ChangeColumnToUser< ActiveRecord::Migration

  # 変更内容(db:migrate時に実行される)
  def up
    change_column :users, :name, :string #nameカラムのデータ型をstringへ変更
  end

  # 変更前の状態(db:rollback時に実行される)
  def down
    change_column :users, :name, :text
  end
end
```

このようにすると変更前と変更後の状況がはっきりするため、ロールバックが可能になる。



# 使えそうなメソッド

```rb:migration.rb
# 指定したテーブルに関して色々な操作を一括でできる'change_tableメソッド'
change_table :products do |t|
  t.remove :description, :name #description,nameカラムを削除
  t.string :part_number #part_numberカラムを追加
  t.index :part_number
  t.rename :upccode, :upc_code #upccodeカラムをupc_codeへ変更
end


# カラムの順番を特定の位置にしたいとき
class AddColumn3ToUsers < ActiveRecord::Migration
  def change
    add_column :users, :column3, :string, after: :column2 #afterに指定したカラムのすぐ後にカラムを追加する
  end
end
```

※訂正点などあればご指摘いただけると幸いです。

#参考にした記事
[Active Record マイグレーション | Rails ガイド](https://railsguides.jp/active_record_migrations.html)
[rails generate migrationコマンドまとめ | Qiita](https://qiita.com/zaru/items/cde2c46b6126867a1a64)