はじめに
こんにちは!
見習いエンジニアの 古内 です!
以前、一度作成したモデルを削除し、再度同じ名前モデルを作成したときにうまくいかず、詰まってしまったことがあったので、その解決方法を実演しながら解説をさせていただきます。
私自身も初心者なのでこの解決方法は完璧ではないかもしれませんが、慣れていない人でもわかりやすいように説明できるようがんばります!
環境
- Ruby 2.5.1
- Ruby on Rails 5.2.2
- Docker
- Doker Compose
実演
準備
では環境の準備から始めましょう。
私はこちらを参考に環境構築をしました。
簡単に Rails 開発環境の構築ができるので、みなさんも活用してみてください!( こちらを使うときは最後の Docker Compose の起動まで進めてください )
作業開始
まずはモデルを作成し、
$ rails g model User
マイグレートします。
$ rails db:migrate
User モデルができました。
class User < ApplicationRecord
end
これを一度削除します。
$ rails destroy model User
再度 User モデルを作成してみましょう。
$ rails g model User
マイグレートします。
$ rails db:migrate
すると、
== 20190321053454 CreateUsers: migrating ======================================
-- create_table(:users)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateTable: ERROR: リレーション"users"はすでに存在します
: CREATE TABLE "users" ("id" bigserial primary key, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
このようなエラー文が出てしまい、 User モデルを作成できません。
対策
User モデルを削除したときに、それが DB 側に伝わっていなかったことが問題だったです。
では解決してみましょう!
まず作成したマイグレーションファイルを削除します。
$ rails destroy migration create_users
続いて DB 側にモデルを削除したことを伝えるマイグレーションファイルを作成します。
$ rails g migration drop_table_users
続いて作成したマイグレーションファイルに以下のように追加してください。
class DropTableUsers < ActiveRecord::Migration[5.2]
def change
drop_table :users #追加
end
end
これを追記することで DB 側のテーブルを削除できます。
マイグレートします。
$ rails db:migrate
先程使ったマイグレーションファイルを削除しましょう。
$ rails destroy migration drop_table_users
ではもう一度 User モデルを作成し、マイグレートしてみましょう。
$ rails g model user
$ rails db:migrate
これで User モデルを再度作成できました!
解説
今回の原因は User モデルを削除したときに、 DB 側にそれが伝わっていなかったことでした。
「なぜ作成するときは大丈夫なのに、削除するときはだめなの?」と思う方もいると思います。
これはマイグレートに原因があります。
図にすると、
Rails 側 migrate DB 側
User model 作成 --------> User table 作成
Rails 側 DB 側
User model を削除 User table が存在
Rails 側 migrate DB 側
User model 作成 --------> User table はすでに存在
このようなことが起きているためエラー文が出てしまったのです。
ここでテーブルを削除するマイグレートを実行すると、
Rails 側 migrate DB 側
User model 作成 -------------> User table 作成
Rails 側 DB 側
User model を削除 User table が存在
Rails 側 migrate DB 側
drop_table_user 作成 ------------> User table を削除
Rails 側 migrate DB 側
User model 作成 -------------> User table 作成
となり、再度 User モデルを作成できるようになります。
ちなみにマイグレートを実行すると、db/migrate ディレクトリにあるマイグレーションファイルを上から逐一 DB に反映させていくので、ここで整合性が取れていないとエラーが起きてしまいます。 ( テーブル削除のマイグレーションファイルを削除したのもこのため。テーブルを削除が完了したら、次のマイグレート実行時、削除対象がないためエラーの原因になる。 )
最後に
マイグレーション周りは複雑でわかりにくいです。
私もモデルとテーブルが別のものだと理解したり、整合性を取るようにするまでは大変でした。
モデル = DB の設計図
テーブル = モデルをもとに実際に作られたもの
と考えればわかりやすいかもしれません。