【Rails】データベースのテーブルを新設し，カラムを編集する

# はじめに
学習中で躓いた箇所を記事にしました．初学者の学習の一助となれば幸いです．
また，内容に不備，間違いがあればご指摘いただければと思います．

### 環境
Ruby 3.1.2
Rails 6.1.7

# 目次
**1. 基礎知識**

**2. データベースにテーブルを新設する**
2-1. マイグレーションファイルを作成する
2-2. マイグレーションファイルをデータベースに適用する
2-3. データベースのテーブル定義を確認する

**3. テーブルのカラムを編集（修正，追加，削除）する**
3-1. カラム名を修正する
3-2. カラムを追加する
3-3. カラムを削除する
3-4. マイグレーションファイルの直接編集によりカラムを編集する
3-5. 極力，マイグレーションファイルは直接編集しない

**4. まとめ**

# 1. 基礎知識
データベースのテーブルカラムを編集するためには，マイグレーションファイルを作成し，データベースへ適用します．マイグレーションファイルとは，データベースのテーブル仕様（カラム名，データ型等）を記載した.rbファイルです．Railsでは，テーブル変更の度にマイグレーションファイルを作成し保存するため，データベースに適用するファイルを選択することでテーブルのバージョン管理ができます．

# 2. データベースにテーブルを新設する
### 2-1. マイグレーションファイルを作成する
まずはテーブル仕様を定義するマイグレーションファイルを作成します．
コマンドで```<クラス名(処理内容+テーブル名)> <カラム名>：<データ型>```を指定します．
ここでは```samples```という名のテーブルを新設```(create)```するため，クラス名は```CreateSamples```です．カラム名は```name```，データ型は```string```とします．

```md:コマンド
rails g migration CreateSamples name:string
```
```md:実行結果
      invoke  active_record
      create    db/migrate/20220925142207_create_samples.rb
```
db/migrateディレクトリに，以下のマイグレーションファイルが作成されました．
```db/migrate/20220925142207_create_samples.rb
class CreateSamples < ActiveRecord::Migration[6.1]
  def change
    create_table :samples do |t|
      t.string :name

      t.timestamps
    end
  end
end
```

### 2-2. マイグレーションファイルをデータベースに適用する
次に以下のコマンドを実行します．
```db:コマンド
rails db:migrate
```
```db:実行結果
== 20220925142207 CreateSamples: migrating ====================================
-- create_table(:samples)
   -> 0.0036s
== 20220925142207 CreateSamples: migrated (0.0037s) ===========================
```
データベースにテーブルが追加されました．
テーブルへ反映された内容は以下のファイルで確認ができます．
```db/schema.rb
ActiveRecord::Schema.define(version: 2022_09_25_142207) do
  create_table "samples", force: :cascade do |t|
    t.string "name"
    中略
  end
end
```
```name```カラムが```string```型で適用されました．

### 2-3. データベースのテーブル定義を確認する
テーブルに確実に反映されているかはデータベースのコンソールで確認します．
```db:コマンド
rails dbconsole
```
```db:実行結果
SQLite version 3.36.0 2021-06-18 18:58:49
Enter ".help" for usage hints.
sqlite> 
```
table一覧を確認するため，以下のコマンドを実行します．
```db:コマンド
sqlite> .table
```
```db:実行結果
ar_internal_metadata  samples               schema_migrations
```
samplesテーブルが確認できました．
次にテーブルの定義を確認します．
```db:コマンド
sqlite> pragma table_info(samples);
```
```db:実行結果
0|id|integer|1||1
1|name|varchar|0||0
（略）
```
マイグレーションファイルの通り，テーブルが定義されていることが確認できました．
以下のコマンドでコンソールを終了します．
```db:コマンド
sqlite> .exit
```

# 3. テーブルのカラムを編集（修正，追加，削除）する
### 3-1. カラム名を修正する
対応の流れは，テーブル新設時と同様です．
```マイグレーションファイルの作成　→ マイグレーションファイルの内容確認，必要があれば編集　→ データベースへ適用　→ 適用状態の確認```

ここではカラム名```name```を```nickname```に変更します．
カラム名を変更する場合は，```Rename<現在のカラム名>To<変更後のカラム名>In<テーブル名>```を指定します．
```db:コマンド
rails g migration RenameNameToNickNameInSamples
```
```db:実行結果
      invoke  active_record
      create    db/migrate/20220925142914_rename_name_to_nick_name_in_create_samples.rb
```
作成されたマイグレーションファイルに変更内容を記載します．
```rename_column :<テーブル名>, :<現在のカラム名>, :<変更後のカラム名>```をchangeメソッドに追記します．
```db/migrate/20220925142914_rename_name_to_nick_name_in_create_samples.rb
class RenameNameToNickNameInSamples < ActiveRecord::Migration[6.1]
  def change
    rename_column :samples, :name, :nickname #ここに変更内容を追記．
  end
end
```
変更内容をデータベースへ適用します．
```db:コマンド
rails db:migrate
```
適用結果を確認します．
```db/schema.rb
ActiveRecord::Schema.define(version: 2022_09_25_142914) do
  create_table "samples", force: :cascade do |t|
    t.string "nickname" #nameがnicknameに変更されました．
    （略）
  end
end
```
データベースのテーブル定義も確認します．
```db:コマンド
$ rails dbconsole
sqlite> pragma table_info(samples);
```
```db:実行結果
0|id|integer|1||1
1|nickname|varchar|0|NULL|0
（略）
```
テーブル定義も```name```から```nickname```に変更されていることが確認できました．


### 3-2. カラムを追加する
ここではカラム```age```を追加します．
カラムを追加する場合は，```Add<追加するカラム名>To<テーブル名> <カラム名>:<データ型> <カラム名>:<データ型>,,,,```を指定します．
```db:コマンド
rails g migration AddAgeToSamples
```
```db:実行結果
      invoke  active_record
      create    db/migrate/20220925151011_add_age_to_samples.rb
```
作成されたマイグレーションファイルの内容を確認します．
```add_column :<テーブル名>, :<削除するカラム名>, :<データ型>```がchangeメソッドに記述されています．
内容に不備があれば```rails db:migrate```する前に修正します．
```db/migrate/20220925151011_add_age_to_samples.rb
class AddAgeToSamples < ActiveRecord::Migration[6.1]
  def change
    add_column :samples, :age, :integer
  end
end
```
変更内容をデータベースへ適用します．
```db:コマンド
rails db:migrate
```
```db:実行結果
== 20220925151011 AddAgeToSamples: migrating ==================================
-- add_column(:samples, :age, :integer)
   -> 0.0036s
== 20220925151011 AddAgeToSamples: migrated (0.0038s) =========================
```
適用結果を確認します．
```db/schema.rb
ActiveRecord::Schema.define(version: 2022_09_25_151011) do
  create_table "samples", force: :cascade do |t|
    t.string "nickname"
    （略）
    t.integer "age"
  end
end
```
```age```カラムが追加されました．
データベースのテーブル定義も確認します．
```db:コマンド
$ rails dbconsole
sqlite> pragma table_info(samples);
```
```db:実行結果
0|id|integer|1||1
1|nickname|varchar|0|NULL|0
（略）
4|age|integer|0||0
```
テーブル定義でも```age```カラムが追加されていることを確認できました．

### 3-3. カラムを削除する
ここではカラム```age```を削除します．
カラムを削除する場合は，```Remove<削除するカラム名>From<テーブル名> <カラム名>:<データ型> <カラム名>:<データ型>,,,,```を指定します．
```db:コマンド
rails g migration RemoveAgeFromSamples age:integer
```
```db:実行結果
      invoke  active_record
      create    db/migrate/20220925152911_remove_age_from_samples.rb
```
作成されたマイグレーションファイルの内容を確認します．
```remove_column :<テーブル名>, :<削除するカラム名>, :<データ型>```がchangeメソッドに記述されています．
内容に不備があれば```rails db:migrate```する前に修正します．
```db/migrate/20220925152911_remove_age_from_samples.rb
class RemoveAgeFromSamples < ActiveRecord::Migration[6.1]
  def change
    remove_column :samples, :age, :integer
  end
end
```
変更内容をデータベースへ適用します．
```db:コマンド
rails db:migrate
```
```db:実行結果
== 20220925152911 RemoveAgeFromSamples: migrating =============================
-- remove_column(:samples, :age, :integer)
   -> 0.0279s
== 20220925152911 RemoveAgeFromSamples: migrated (0.0280s) ====================
```
適用結果を確認します．
```db/schema.rb
ActiveRecord::Schema.define(version: 2022_09_25_152911) do
  create_table "samples", force: :cascade do |t|
    t.string "nickname"
    （略）
  end
end
```
```age```カラムが削除されました．
データベースのテーブル定義も確認します．
```db:コマンド
$ rails dbconsole
sqlite> pragma table_info(samples);
```
```db:実行結果
0|id|integer|1||1
1|nickname|varchar|0|NULL|0
（略）
```
テーブル定義でも```age```カラムが削除されていることを確認できました．

### 3-4. マイグレーションファイルの直接編集によりカラムを編集する
マイグレーションファイルを直接編集するためには，```rails db:rollback```または```rails db:migrate:down VERSION=**************```でロールバックする必要があります．
ロールバックにより，データベースに未適用状態となるため，マイグレーションファイルを直接編集できるようになります．（データベースに適用されている状態のマイグレーションファイルを直接編集して，```rails db:migrate```しても変更内容はデータベースに反映されません．）

以下にマイグレーションファイルの直接編集例を載せます．
カラム名```nickname```を```familyname```に変更することを考えます．

まず，既存のマイグレーションファイルのデータベースへの適用状態を確認します．
```db:コマンド
rails db:migrate:status
```
```db:実行結果
（略）
 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20220925142207  Create samples
   up     20220925150043  Rename name to nick name in samples
   up     20220925151011  Add age to samples
   up     20220925152911  Remove age from samples
```
カラム名を```Rename```したマイグレーションファイルは上から2段目です．```Status```が```up```になっています．データベースに適用されていますので，以下のコマンドでロールバックを行います．
```db:コマンド
rails db:migrate:down VERSION=20220925150043
```
```db:実行結果
== 20220925150043 RenameNameToNickNameInSamples: reverting ====================
-- rename_column(:samples, :nickname, :name)
   -> 0.0165s
== 20220925150043 RenameNameToNickNameInSamples: reverted (0.0202s) ===========
```
ロールバックが実行されましたので，再度データベースへの適用状態を確認します．
```db:コマンド
rails db:migrate:status
```
```db:実行結果
（略）
 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20220925142207  Create samples
  down    20220925150043  Rename name to nick name in samples
   up     20220925151011  Add age to samples
   up     20220925152911  Remove age from samples
```
目的のファイルのみ```Status```が```down```となっています．
ここでファイルの直接編集が可能になります．
以下のようにカラム名を```nickname```から```familyname```に変更します．
```db/migrate/20220925150043_rename_name_to_nick_name_in_samples.rb
class RenameNameToNickNameInSamples < ActiveRecord::Migration[6.1]
  def change
    rename_column :samples, :name, :familyname #nickname → familynameに変更
  end
end
```
変更が完了したらデータベースへ適用します．
```db:コマンド
rails db:migrate
```
```db:実行結果
== 20220925150043 RenameNameToNickNameInSamples: migrating ====================
-- rename_column(:samples, :name, :familyname)
   -> 0.0150s
== 20220925150043 RenameNameToNickNameInSamples: migrated (0.0151s) ===========
```
スキーマファイルとテーブル定義を確認します．
```db/schema.rb
ActiveRecord::Schema.define(version: 2022_09_25_164433) do
  create_table "samples", force: :cascade do |t|
    t.string "familyname"
    （略）
  end
end
```
カラム名が```nickname```から```familyname```に変更されました．
データベースのテーブル定義も確認します．
```db:コマンド
$ rails dbconsole
sqlite> pragma table_info(samples);
```
```db:実行結果
0|id|integer|1||1
1|familyname|varchar|0|NULL|0
中略
```
カラム名が```familyname```に編集されていることを確認できました．

### 3-5. マイグレーションファイルの直接編集は非推奨です
マイグレーションファイルを直接編集する方法を載せましたが，この方法は推奨されてません．
Railsガイドに記載がある通り，テーブルカラムの編集を行う場合は，マイグレーションファイルを新設することが基本方針です．
以下，[Railsガイド](https://railsguides.jp/active_record_migrations.html#%E6%97%A2%E5%AD%98%E3%81%AE%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%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)から引用です．
>一般に、既存のマイグレーションを直接変更するのはよくありません。既存のマイグレーションを変更すると、自分自身はもちろん、共同作業者も余分な作業を強いられます。さらに、既存のマイグレーションがproduction環境で実行中の場合、ひどい頭痛の種になるでしょう。既存のマイグレーションを直接修正するのではなく、修正用のマイグレーションを新たに作成してそれを実行するのが正しい方法です。これまでコミットされてない（より一般的に言えば、これまでdevelopment環境以外にデプロイされたことのない）マイグレーションを新たに生成し、それを編集するのが害の少ない方法です。

# 4. まとめ
- マイグレーションファイルを新設し，データベースに適用することで，テーブルを新設し，カラムを編集できます．
- マイグレーションファイルの直接編集によるカラムの編集は非推奨です．

以上です．
皆さんの学習の一助となれば幸いです．

# 参考

https://railsguides.jp/active_record_migrations.html

https://www.amazon.co.jp/gp/product/4839962227/ref=ppx_yo_dt_b_asin_image_o01_s00?ie=UTF8&psc=1

https://prograshi.com/framework/rails/migration-edit-table-and-column/

https://www.sejuku.net/blog/14229

https://qiita.com/s_tatsuki/items/3e1f119c91e21b8f0c33

https://qiita.com/ISSO33/items/33a935cb3255c269bef2

https://qiita.com/Qiita/items/c686397e4a0f4f11683d#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%81%AE%E5%9F%8B%E3%82%81%E8%BE%BC%E3%81%BF