はじめに
Railsでアプリを作成中、gem sorceryでユーザー認証機能を追加しようとしたときに起きた出来事と、そのときに学んだ教訓です。
結論
-
rails generate sorcery:install
を実行するとUserモデルも自動的に作成されるので、すでにUserモデルを作っている場合は要注意 - 公式をよく読みましょう。
https://github.com/Sorcery/sorcery
何があった?
Railsで掲示板アプリを作成していました。User、Post、Commentなどのモデルを作成したところで、「そういえばsorceryってgemでログイン機能作れるんだったな〜」と軽い気持ちでgemをbundle installし、rails generate sorcery:install
したところ…
web-1 | == 20240827051328 SorceryCore: migrating ======================================
web-1 | -- create_table(:users)
web-1 | bin/rails aborted!
web-1 | StandardError: An error has occurred, all later migrations canceled: (StandardError)
web-1 |
web-1 | Mysql2::Error: Table 'users' already exists
web-1 | /myapp/db/migrate/20240827051328_sorcery_core.rb:3:in `change'
web-1 |
web-1 | Caused by:
web-1 | ActiveRecord::StatementInvalid: Mysql2::Error: Table 'users' already exists (ActiveRecord::StatementInvalid)
web-1 | /myapp/db/migrate/20240827051328_sorcery_core.rb:3:in `change'
web-1 |
web-1 | Caused by:
web-1 | Mysql2::Error: Table 'users' already exists (Mysql2::Error)
web-1 | /myapp/db/migrate/20240827051328_sorcery_core.rb:3:in `change'
web-1 | Tasks: TOP => db:prepare
web-1 | (See full trace by running task with --trace)
web-1 exited with code 1
「usersテーブルはすでに存在しているためmigrateできませんでした」
え、sorceryてUserモデルも自動で作るの??
→公式に書いてあった〜〜〜
Run the following command to generate the core migration file, the initializer file and the
User
model class.
$ rails generate sorcery:install
https://github.com/Sorcery/sorcery
きちんと読みましょうね。
そんなわけで、Userモデルをすでに作成した状態でSorceryをインストールしてしまったためにマイグレーションエラーが発生しました。
対処方法
【方針】
最初にusersテーブルを作成したmigrationを取り下げ、Sorceryで作成されたmigrationファイルを採用する
【やったこと】
usersテーブルを作成した時のmigrationを取り下げようとしました。
$ rails db:migrate:down VERSION=usersテーブル作成時のmigrationID
が、他のpostsテーブルなどでusersのforeign keyを設定していたためエラーが発生。
Mysql2::Error: Cannot drop table 'users' referenced by a foreign key constraint 'fk_rails_5b5ddfd518' on table 'posts'.
そこで、関連するテーブルのmigrationを1つずつdownしていき、既存のusersテーブルもdown後、Sorceryのusersテーブルのmigrationをupし、関連するテーブルを再度up。
# 作業前
Status Migration ID Migration Name
--------------------------------------------------
up 20240826063633 Create users
up 20240826064309 Create posts
up 20240826064450 Create likes
up 20240827003449 Create comments
down 20240827051328 Sorcery core
# 作業後
Status Migration ID Migration Name
--------------------------------------------------
down 20240826063633 Create users
up 20240826064309 Create posts
up 20240826064450 Create likes
up 20240827003449 Create comments
up 20240827051328 Sorcery core
この時点でlocalhost:3000にアクセスすると、ActiveRecord::PendingMigrationError
が発生しました。適用されていないmigrationがあるとエラーになるのですね。
よくないとは思いつつ、今回は該当のmigrationファイルを削除して対応しました。
Status Migration ID Migration Name
--------------------------------------------------
up 20240826064309 Create posts
up 20240826064450 Create likes
up 20240827003449 Create comments
up 20240827051328 Sorcery core
migrationのおさらい
migrationのupとdown
下記コマンドを打つとmigrationファイルが作成されます。
$ rails generate migration ファイル名 カラム名:型
この時点では、migrationファイルが作成されただけで、DBに適用はされていない(down)状態です。
$ rails g migration AddNameToUser name:string
invoke active_record
create db/migrate/20240828022300_add_name_to_user.rb
$ rails db:migrate:status
database: myapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20240826064309 Create posts
up 20240826064450 Create likes
up 20240827003449 Create comments
up 20240827051328 Sorcery core
down 20240828022300 Add name to user
ここでrails db:migrate
を実行することでStatusがupになり、migrationが適用されます。
migrationを実行するコマンド
上記の通り、migrationファイルが作成されていても、migrationの状態がupになっていないとDBには適用されません。このup/downを実行するコマンドには例えば以下のようなものがあります。
$ rails db:migrate
# まだ実行されていないchangeまたはupメソッドを日付が古い順から実行していく
$ rails db:rollback
# 直前に行ったマイグレーションをロールバックして取り消す(down状態にする)
$ rails db:rollback STEP=3
# 最後に行った3つのマイグレーションをdown状態にする
$ rails db:migrate:down VERSION=migrationID
# 特定のマイグレーションをdown状態にする
$ rails db:migrate:up VERSION=migrationID
# 特定のマイグレーションをup状態にする
補足
up状態のmigrationファイルを削除してはいけません。←やった人
$ rails db:migrate:status
Status Migration ID Migration Name
--------------------------------------------------
up 20240826063633 Create users
up 20240826064309 Create posts
up 20240826064450 Create likes
up 20240827003449 Create comments
up 20240827045550 ********** NO FILE **********
migration up状態でファイルを削除してしまうと、rollbackでmigrationをdownすることもできなくなってしまいます。
$ rails db:rollback
bin/rails aborted!
ActiveRecord::UnknownMigrationVersionError: (ActiveRecord::UnknownMigrationVersionError)
No migration with version number 20240827045550.
ファイルを元に戻してロールバックするなどで対処しましょう。
今後気をつけること
- gemを使う時は、公式をよく読む!
- 通常migrationファイルを修正したい時は、新規でmigrationファイルを作成して変更を適用する
- 本来、migrationファイルは更新履歴として残しておくべきものなので、基本削除しない
参考