開発中のアプリケーションでいらないモデルを削除しようとなったときに、rails d modelをしてしまい(もちろんpushはしてない)「そのやり方は100%ダメだ」と言われることがあったので自戒の念を込めて記事を作成します。
rails newで実験用アプリを用意する
rails new migration_experiment
cd migration_experiment
rails db:create
User modelを作成する
rails g model User name:string
rails db:migrate
Task modelを作成する
rails g model Task name:text description:text
rails db:migrate
User modelにemailカラムを追加する
アプリを作成中User modelに新しくemailカラムを追加することになったと仮定してemailカラムを追加するmigrationファイルを作成します。
rails g migration add_email_to_users
class AddEmailToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :email, :string
end
end
rails db:migrate
この時点でのmigrationファイルの状態が以下です。
20220211071814_create_users.rb
20220211071830_create_tasks.rb
20220211072145_add_email_to_users.rb
rails d model を実行する
さて、ここからが問題のrails d modelの実行です。
rails d model User
こちらを実行すると、migrationファイルの状態がこのようになります。
20220211071830_create_tasks.rb
20220211072145_add_email_to_users.rb
raisl d model前には存在していたUser modelを作成した際のmigrationファイルがなくなっています。この状態でrails db:migrate:resetを実行するとどうなるでしょうか?
== 20220211071830 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0022s
== 20220211071830 CreateTasks: migrated (0.0023s) =============================
== 20220211072145 AddEmailToUsers: migrating ==================================
-- add_column(:users, :email, :string)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: no such table: users
User modelを削除してしまったので、emailカラムを追加するmigrationファイルが適用できずエラーが出てしまっています。
なぜこのようなことが起こってしまうかというと、「rails d model」では、
remove db/migrate/20220211073521_create_users.rb
remove app/models/user.rb
<!--その他の削除されるファイルは割愛-->
の様にmodel自体とmodelを作成したmigrationファイルのみを削除し、そのモデルに新しいカラムを追加したファイルは削除されません。なのでmigrationファイルの整合性が取れなくなるためエラーが発生してしまうことになります。
今は実験のため3つのmigration fileを一人でいじっているだけなので他のmaigrationファイルを全て削除し、新しくモデルを作成(新しいmigrationファイルの作成)後、migrate:resetをかければ元通りの状態までは簡単に持っていけます。しかしチーム開発や、model数が多くなってきた場合そういった作業をするわけにはいきません。
では、不要になったmodelを削除する場合はどうすればいいのでしょうか?
モデルを削除するmigrationファイルを作成する
安全にモデルを削除するにはそのモデルを削除することを宣言するmigrationファイルを作成します。
rails g migration drop_table_users
ファイルを編集します。
class DropTableUsers < ActiveRecord::Migration[6.1]
def change
drop_table :users do |t|
t.string :name
t.string :email
t.timestamps null: false
end
end
end
rails db:migrate
このような作業を経ることによってmigrationファイルの整合性が保たれ、後に新しいmodel作成をしたmigrationファイルが積み上がってきたり、rails db:migrate:resetを実行したときにもエラーを起こさず進めていくことができます。
おまけ
今回モデルを綺麗に削除してmigrationファイルの整合性を保つような作業をしましたが、一度modelを削除したり、rails db:migrate:resetをかけると当然の様にDBにあるデータは戻ってきません。一度dumpしたファイルの内容を全て消してしまいとてもめんどくさいことになったことがあるので皆さんご注意を。