Railsで外部キー制約が貼られたモデル名変更の方法

モデル名を変更したい場合、該当デーブルだけを変更する場合は特に何も考慮に入れる必要はないが、該当テーブルを参照する子モデルがあった場合、子モデルの外部キーも変更する必要があります。
まだ未公開のプロジェクトの場合は、既存のmigrationファイルを書き換えて`rails db:migrate:reset`をすれば良いですが、既にデータが存在しているプロジェクトではそうもいきません。

## モデルの準備
まずはモデルを準備します。今回はUser has_many Tasksの関係であるUserモデルとTaskモデルを用意を用意します。
モデルの中身をわかりやすくするため、今回は外部キー以外のカラムは書かないことにします。

`rails g model User`
`rails g model Task user:references`

上記のコマンドで下記2つのマイグレーションファイルが作成されます。

```ruby:db/migration/20201004121521_create_users.rb

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|

      t.timestamps
    end
  end
end
```

```ruby:db/migration/20201004121556_create_tasks.rb

class CreateTasks < ActiveRecord::Migration[6.0]
  def change
    create_table :tasks do |t|
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
  end
end
```

`rails db:migrate`でschema.rbが更新されます。

```ruby:db/schema.rb

ActiveRecord::Schema.define(version: 2020_10_04_121556) do

  create_table "tasks", force: :cascade do |t|
    t.integer "user_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["user_id"], name: "index_tasks_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  add_foreign_key "tasks", "users"
end
```

railsではreference型を与えると勝手にindexが貼られます。
`index: true`としなくても`t.index ["user_id"], name: "index_tasks_on_user_id"`が記述されていますね。
また、`foreign_key: true`が付与されていたため、`add_foreign_key "tasks", "users"`が記述されていますね。

以上、Userモデルとそれに紐づくTaskモデルが作成されました。

## モデル名を変更する
それでは、UserモデルをAdminUserに変更しましょう。
まずは普通にモデル名を変更するmigrationファイルを作成し、migrateします。

`rails g migration RenameUserToAdminUser`で下記ファイルを作成し、`rails db:migrate`

```ruby:db/migration/20201004123215_rename_user_to_admin_user.rb

class RenameUserToAdminUser < ActiveRecord::Migration[6.0]
  def change
    rename_table :users, :admin_users
  end
end
```


それではschema.rbを確認しましょう。ご覧の通り、
①userはadmin_userに名称変更されています。

しかし、
②taskモデルの外部キーはuser_idのままで変名されていません。（当たり前といえば当たり前）

そして、
③外部キー制約部分はuser_idというカラム名のまま、admin_userに紐付けされています。


```ruby:db/schema.rb

ActiveRecord::Schema.define(version: 2020_10_04_123215) do

  create_table "admin_users", force: :cascade do |t| # ←①
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "tasks", force: :cascade do |t|
    t.integer "user_id", null: false # ←②
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["user_id"], name: "index_tasks_on_user_id"
  end

  add_foreign_key "tasks", "admin_users", column: "user_id" # ←③
end
```

このschemaを確認すれば、外部キー制約のついたカラム名を変更すれば一連の修正が完成しそうですね！

## 外部キーカラム名を変更する
それでは、カラム名変更をするmigrationファイルを作成していきます。
`rails g migration RenameUserIdToAdminUserId`

```ruby:db/migration/20201004125441_rename_user_id_to_admin_user_id.rb

class RenameUserIdToAdminUserId < ActiveRecord::Migration[6.0]
  def change
    rename_column :tasks, :user_id, :admin_user_id
  end
end
```

`rails db:migrate`

カラム名変更が反映されましたね。
④Taskモデルのuser_idがadmin_user_idになり、
⑤外部キーのcolumn名指定がなくなりました。

```ruby:db/schema.rb

ActiveRecord::Schema.define(version: 2020_10_04_125441) do

  create_table "admin_users", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "tasks", force: :cascade do |t|
    t.integer "admin_user_id", null: false # ←④
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["admin_user_id"], name: "index_tasks_on_admin_user_id"
  end

  add_foreign_key "tasks", "admin_users" # ←⑤
end
```

## まとめ
外部キー制約が貼られたモデル名を変更するには、
1. モデル名を変更する
2. 該当モデルへの外部キー名を変更する

これだけでいけちゃいました。案外簡単でしたね。
特にモデル名変更するだけで外部キーの向き先も変えてくれるのはさすがRailsだな〜って感じでした。