シンプルでやさしいチートシート 【Rails Migration編】

## はじめに
毎回ググってる気がするので、まとめました。
右側の目次からお探しの用語やコマンドをクリックしてお使いください！:slight_smile:

## 対象となる読者
* マイグレーションに苦手意識のある初心者の方
* 復習も兼ねて基本操作を中心におさらいしたい経験者の方
* マイグレーション関連でハマりたくない方

###テーブルを作る
modelを作成すると/db/migrate/ 以下にマイグレーションファイルも一緒に作成されます。
この場合クラス名は`User`、`name`,`email`という属性にデータタイプ(型)として`string`を指定しています。

```ruby 

  $ rails g model User name:string email:string
  invoke  active_record
  create    db/migrate/20200130031151_create_users.rb　←　マイグレーションファイル
  create    app/models/user.rb
  invoke    test_unit
  create      test/models/user_test.rb
  create      test/fixtures/users.yml
  
```
マイグレーションファイルには先ほど指定したクラス、changeメソッドの中にテーブル名(指定したモデルの複数形になります)、属性(カラム)、データタイプ(型)などが記載されており、db:migrateコマンドで適用させると、この設計図でテーブルが作成されます。

```ruby
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
```


##### データタイプ(型)の種類
>
* `string` : 文字列
* `text` : 長い文字列
* `integer` : 整数
* `float` : 浮動小数
* `decimal` : 精度の高い小数
* `datetime` : 日時
* `timestamp` : タイムスタンプ
* `time` : 時間
* `date` : 日付
* `binary` : バイナリデータ
* `boolean` : Boolean

※[rails generate migrationコマンドまとめ @zaru ](https://qiita.com/zaru/items/cde2c46b6126867a1a64#%E6%8C%87%E5%AE%9A%E3%81%A7%E3%81%8D%E3%82%8B%E5%9E%8B)

##### カラム修飾子(オプション)
>
* `limit`: string/text/binary/integerフィールドの最大サイズを設定します。
* `precision`: decimalフィールドの精度 (precision) を定義します。この精度は、その数字の総桁数で表されます。
* `scale`: decimalフィールドの精度 (スケール: scale) を指定します。この精度は小数点以下の桁数で表されます。
* `polymorphic`: belongs_to関連付けで使うtypeカラムを追加します。
* `null`: カラムでNULL値を許可または禁止します。
* `default`: カラムでのデフォルト値の設定を許可します。dateなどの動的な値を使う場合は、デフォルト値は最初 (マイグレーションが実行された日付など) にしか計算されないことに注意してください。
* `index`: カラムにインデックスを追加します。
* `comment`: カラムにコメントを追加します。


ちなみに`null`と`default`はコマンドラインで指定できません。

 
[Railsガイド カラム修飾子](https://railsguides.jp/active_record_migrations.html#%E3%82%AB%E3%83%A9%E3%83%A0%E4%BF%AE%E9%A3%BE%E5%AD%90)
### データタイプ(型)、カラム修飾子(オプション)を指定してテーブルを作る

```ruby
  $ rails g model Product name:string:index sku:string{10}:uniq \
                 supplier:references{polymorphic} 'price:decimal{10,2}'                 
```
`index`は追加したいカラムに記述するだけです。
`limit`や`polymorphic`は{}内に記述。
`precision`、`scale`はカラムとdecimal型、引数{precisionの値,scaleの値}を``で括ると適用されました。

**20200131044330_create_products.rb**

```ruby
class CreateProducts < ActiveRecord::Migration
  def change
    create_table :products do |t|
      t.string :name
      t.string :sku, limit: 10
      t.references :supplier, polymorphic: true
      t.decimal :price, precision: 10, scale: 2

      t.timestamps
    end
    add_index :products, :name
    add_index :products, :sku, unique: true
  end
end
```
db:migrateで適用させるとテーブルが作成されます。
**schema.rb**

```ruby
  create_table "products", force: :cascade do |t|
    t.string "name"
    t.string "sku", limit: 10
    t.string "supplier_type"
    t.bigint "supplier_id"
    t.decimal "price", precision: 10, scale: 2
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["name"], name: "index_products_on_name"
    t.index ["sku"], name: "index_products_on_sku", unique: true
    t.index ["supplier_type", "supplier_id"], name: "index_products_on_supplier_type_and_supplier_id"
  end
```

普通にマイグレーションファイルを作成し、後でオプション記述する方が早くて確実な気もしますが、お好みでどうぞ。
### テーブルを削除する

usersテーブルを削除します。

```ruby
  $ rails g migration DropUsers　→　マイグレーションファイルを削除する場合
  $ rails destroy model User　→　モデルを削除する場合(マイグレーションファイルを含む、rails g model Userで作成されたファイル全てを削除。)
```
今回はマイグレーションファイルのみ削除したいので、`$ rails g migration DropUsers`を実行します。
実行すると空のChangeメソッドが生成されます。
ここに`drop_table :テーブル名`と書いてdb:migrateコマンドで適用させると削除できます。
ただこれだとrollbackが効きません。(元に戻せない)

```ruby
class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users
  end
end
```
rollbackさせるには

*  changeメソッドでrollbackを効かせるためにブロックを渡す。
*  upメソッド、downメソッドを使う。


```ruby
class DropUsers < ActiveRecord::Migration
  def change
    drop_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
```

```ruby
class DropUsers < ActiveRecord::Migration
  def up
    drop_table :users
  end
  def down
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
  end
```
これであれば`rails db:migrate`で削除、やっぱり戻したいとなっても`rails db:rollback`でrollbackできますので安心して削除できます。


db:migrateやdb:rollback、changeメソッドのrollbackについては[マイグレーションあれこれ](https://qiita.com/kosuke_n/items/d2e6d18f1c501cf63d42#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%82%E3%82%8C%E3%81%93%E3%82%8C )で詳しく解説しております。


###テーブル名を変更する

```ruby
  $ rails g migration RenameProducts
```
実行すると空のチェンジメソッドが生成されますので、
中に`rename_table :既存のテーブル名, :変更後のテーブル名`を記載しdb:migrateコマンドで適用させるとテーブル名が変更されます。


```ruby
class RenameProducts < ActiveRecord::Migration
  def change
    rename_table :products, :goods
  end
end
```
###カラムを追加する、オプションを追加する。
usersテーブルに`admin`カラム、データタイプ(型)は`boolean`を指定し、追加してみます。その後生成されたマイグレーションファイルに`default: false`オプションを記述します。

```ruby

  $ rails g migration (AddAdminToUsersもしくはadd_admin_to_users) admin:boolean
```

生成されたマイグレーションファイルに`default: false`を記述すれば、db:migrateで適用できます。


**20200131051723_add_boolean_to_users.rb**

```ruby
class AddAdminToUsers < ActiveRecord::Migration
  def change
    add_column :users, :admin, :boolean, default: false
  end
```

### 既存のカラムに変更を加える
usersテーブルに`profile:string`というカラムがあったとします。それを`profile:text`に変更します。

```ruby

  $ rails g migration (ChangeProfileDatatypeOfUsersもしくはchange_profile_datatype_of_users)
```
空のchangeメソッドが生成されますが、rollbackを効かせるために、up,downメソッドを使用します。db:migrateで変更が適用されます。

```ruby
class ChangeProfileDatatypeOfUsers< ActiveRecord::Migration
  # 変更内容
  def up
    change_column :users, :profile, :text
  end
  # 変更前の状態
  def down
    change_column :users, :profile, :string
  end
end
```

###カラムを削除する
usersテーブルの`profile:text`というカラム削除します。

```ruby
  $ rails g migration (RemoveProfileFromUsersもしくはremove_profile_from_users) profile:text
```
実行すると空のchangeメソッドが生成されています。
ここに`remove_column :テーブル名, カラム名`と書いてdb:migrateコマンドで適用させると削除できます。
ただこれだとrollbackが効きません。(元に戻せない)

```ruby
class RemoveProfileFromUsers < ActiveRecord::Migration
  def change
    remove_column :users, :profile
  end
end
```

rollbackさせるには

*  changeメソッドでrollbackを効かせるためにオプション(型)を指定する。
*  従来のupメソッド、downメソッドを使う。

rails db:migrateで変更を適用できます。

```ruby
class RemoveProfileFromUsers < ActiveRecord::Migration
  def change
    remove_column :users, :profile, :text
  end
end
```


```ruby
class RemoveProfileFromUsers < ActiveRecord::Migration
# 変更内容
  def up
    remove_column :users, :profile, :text
  end
# 変更前の内容
  def down
    add_column :users, :profile, :text
  end
end
```

これであれば`rails db:migrate`で削除、やっぱり戻したいとなっても`rails db:rollback`でrollbackできますので安心して削除できます。


db:migrateやdb:rollback、changeメソッドのrollbackについては[マイグレーションあれこれ](https://qiita.com/kosuke_n/items/d2e6d18f1c501cf63d42#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%82%E3%82%8C%E3%81%93%E3%82%8C )で詳しく解説しております。

###カラム名を変更する
postsテーブルの`image`カラムを`picture`カラムに変更してみます。

```ruby
  $ rails g migration (RenameImageToUsersもしくはrename_image_to_users)
```
`change`メソッドが生成されます。rollbackを効かしたい場合は、`up,down`メソッドで書きましょう。db:migrateで変更を適用できます。

```ruby
class RenameImageToUsers < ActiveRecord::Migration
  # 変更内容
  def up
    rename_column :users, :image, :picture
  end
  # 変更前の内容
  def down
    rename_column :users, :picture, :image
  end
end
```

### マイグレーションファイルの名前の付け方
カラムを追加、削除、変更する場合、`AddColumnToUsers`や`ChangeColumnOfUsers`、
`RemovColumnFromUsers`といった名前をつけてしまうと、同テーブルのカラムを追加、変更、削除したくなった時に名前が被る可能性があるので、

* `AddAdminToUsers`(usersテーブルにadminカラムを追加する)
* `ChangeProfileDatatypeOfUsers`(usersテーブルのプロフィールカラムのデータタイプを変更する)
* `RemoveProfileFromUsers`(usersテーブルからプロフィールからむを削除する)
といった具合に、より具体的な名前をつける方が良さそうです。

### マイグレーションあれこれ 

#### db:migrate
rails db:migrateを実行すると、基本的にこれまで実行されたことのないchangeまたはupメソッドを実行します。未実行のマイグレーションがない場合は何もせずに終了します。マイグレーションの実行順序は、マイグレーションの日付に基づきます。db/schema.rbスキーマファイルを更新し、スキーマがデータベースの構造に一致するようにします。

#### db:rollback
rails db:rollbackで直前に行ったマイグレーションをロールバックします。
これによりchangeメソッドを逆転実行するかdownメソッドを実行します。

#### db:rollback STEP=()

``` ruby
  $ rails db:rollback STEP=2
``` 
これにより、最後に行った2つのマイグレーションがロールバックされます。
rails db:migrate:statusで状況を確認してコマンド実行しましょう。
#### db:migrate:status

rails db:migrate:statusを実行すると`Status`のup,down(マイグレーションが適用されているかどうか)と`ID`、`マイグレーションファイル名`が閲覧できます。

```ruby

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20200131044330  Create products
   up     20200131051621  Create users
   up     20200131051723  Add boolean to users
   down   20200131054638  Add profile to users
```
#### db:migrate VERSION=()

```ruby
  $ rails db:migrate VERSION=20200131062759
```

マイグレーションの特定のバージョンを指定すると、Active Recordは指定されたマイグレーションに達するまでマイグレーション (change/up/down) を実行します。VERSIONはdb:migrate:statusコマンドで確認できます。

#### db:migrate:redo
> db:migrate:redoコマンドは、ロールバックと再マイグレーションを一度に実行できるショートカットです。複数バージョンに対してこれを行いたい場合は、db:rollbackコマンドの場合と同様にSTEPパラメータを指定することもできます。
[ロールバック Railsガイド](https://railsguides.jp/active_record_migrations.html#%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF)

普段db:migrateする際は、はじめから`rails db:migrate:redo`を実行し、rollbackの動きをを担保しておくといいかもしれません。

#### changeメソッド、up,downメソッド

changeメソッドはいい感じにrollbackを行ってくれて便利なんですが、サポートされているマイグレーション定義を把握してから使用しないと、二度手間になりかねません。
(drop_tableはブロックを渡さなければならない、remove_columnは型を指定しなければならないなど)
無駄なマイグレーションファイルを作成しなくて済むように、使用する場合はサポートされているマイグレーション定義を確認しておきましょう。

> changeメソッドは、マイグレーションを自作する場合に最もよく使われます。このメソッドを使えば、Active Recordがマイグレーションを逆方向に実行 (ロールバック) する方法を自動的に理解してくれるため、多くの場面で利用できます。
[changeメソッドを使う Railsガイド]
(https://railsguides.jp/active_record_migrations.html#change%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%86)

面倒な場合は確実なupメソッド、downメソッドを使用してもいいと思います。現場に合わせて適時使い分けていただければと思います。

## おわりに
もしこの記事が為になった！という方はフォローやいいねボタンのクリックをしていただけたらとても嬉しいです！:smiley:


## 参考
[Railsガイド](https://railsguides.jp/active_record_migrations.html)
[rails generate migrationコマンドまとめ @zaru ](https://qiita.com/zaru/items/cde2c46b6126867a1a64)
[Ruby on Railsでテーブルのカラムを追加・削除する方法 @Reasonable Code](https://reasonable-code.com/rails-column/)