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


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

モデルの準備

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

rails g model User
rails g model Task user:references

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

db/migration/20201004121521_create_users.rb

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

      t.timestamps
    end
  end
end
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が更新されます。

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

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に紐付けされています。

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

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名指定がなくなりました。

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だな〜って感じでした。