はじめに&状況
アプリ開発の課題においてなぜかわかりませんがマイグレーションファイルで認証機能に関するコードを入力してmigrateをしたのにusersテーブルのnameだけがうまくいっていなくて相当悩んでいましたが無事解決できたのでアウトプットの為にも記事にしておこうかと思います。
環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3
私がしたこと
解決までの流れをそのまま記載しておきます
ローカルはOKだがデプロイでFailed
・StandardError: An error has occurred, this and all later migrations canceled: (StandardError)
・PG::DuplicateColumn: ERROR: column "name" of relation "users" already exists
・ActiveRecord::StatementInvalid: PG::DuplicateColumn: ERROR: column "name" of relation "users" already exists (ActiveRecord::StatementInvalid)
・PG::DuplicateColumn: ERROR: column "name" of relation "users" already exists (PG::DuplicateColumn)
上記4つのエラーがデプロイ先で表示されていました。どういうエラーかというと「PostgreSQLデータベースに対して、「users」テーブルに「name」カラムを追加しようとしたが、そのカラムがすでに存在しているよ!」という内容でした。
当時のマイグレーションファイルとスキーマファイルはこんな感じ
マイグレーションファイル1つ目
class SorceryCore < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false, index: { unique: true }
t.string :crypted_password
t.string :salt
t.timestamps null: false
end
end
end
マイグレーションファイル2つ目
class AddNameToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :name, :string, null: false
end
end
スキーマファイルのusersテーブル
create_table "users", force: :cascade do |t|
t.string "email", null: false
t.string "crypted_password"
t.string "salt"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name", null: false
t.index ["email"], name: "index_users_on_email", unique: true
end
なぜusersテーブルのnameに関するマイグレーションが二つあるかというと、1つめのマイグレーションファイルだけではなぜかusersテーブルにt.string "name", null: false
が登録?されなかったので2つ目のマイグレーションファイルを作成
つまり、この2つ目のマイグレーションファイルを削除すれば解決なのか?
とりあえず不要なマイグレーションファイルを削除してみる
1.不要?なマイグレーションファイルの削除
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb578bd2bdf3 sotu_app-web "bash -c 'bundle ins…" 3 days ago Up 15 minutes 0.0.0.0:3000->3000/tcp sotu_app-web-1
b63bae0c1bd7 postgres "docker-entrypoint.s…" 3 days ago Up 15 minutes (healthy) 0.0.0.0:5432->5432/tcp sotu_app-db-1
- コンテナ内に入る(コンテナ名はご自身のものに変えてください)
docker exec -it sotu_app-web-1 /bin/bash
- マイグレーションファイルの状況確認
rails db:status
もしくはこちらのコード
rails db:migrate:status
- 次のような状態で3つ目のupを削除することで解決したい
# rails db:migrate:status
database: myapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20240710044807 Create tasks
up 20240806045143 Sorcery core
up 20240810051914 Add name to users
- 具体的な手順
# rails db:rollback
== 20240810051914 AddNameToUsers: reverting ===================================
-- remove_column(:users, :name, :string, {:null=>false})
-> 0.0084s
== 20240810051914 AddNameToUsers: reverted (0.0176s) ==========================
# rails db:migrate:status
database: myapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20240710044807 Create tasks
up 20240806045143 Sorcery core
down 20240810051914 Add name to users
# rm db/migrate/20240810051914_add_name_to_users.rb
2. マイグレーションファイルの編集
次に、元々存在していた最初のマイグレーションファイル(SorceryCore
)を編集して、name
カラムが確実に含まれているか確認します。
- 20240806045143 Sorcery coreのusersテーブルにname入っているの確認して再度マイグレーションを実行するがスキーマファイルのusersにname関連が追加されない
次の方法で解決しました!
- マイグレーションファイルの適応?がうまくいっていなさそう
- upの残り二つも一度ロールバックして再度migrateしたほうがよさそう
# rails db:migrate:status
database: myapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20240710044807 Create tasks
up 20240806045143 Sorcery core
# rails db:rollback
== 20240806045143 SorceryCore: reverting ======================================
-- drop_table(:users)
-> 0.0192s
== 20240806045143 SorceryCore: reverted (0.0316s) =============================
# rails db:rollback
== 20240710044807 CreateTasks: reverting ======================================
-- drop_table(:tasks)
-> 0.0067s
== 20240710044807 CreateTasks: reverted (0.0183s) =============================
# rails db:migrate
== 20240710044807 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0225s
== 20240710044807 CreateTasks: migrated (0.0228s) =============================
== 20240806045143 SorceryCore: migrating ======================================
-- create_table(:users)
-> 0.0148s
== 20240806045143 SorceryCore: migrated (0.0150s) =============================
- 無事usersテーブルにnameが追加された!最後にpushとかしておく
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.string "crypted_password"
t.string "salt"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
end
さいごに
データベース周りは後々エラーが起こると大変、という事なので。まだ初めの段階だから2回のロールバックで済んでよかった。
ローカルでOKでもデプロイでのFailedは逐一確認して進めていきたいと思います!
今回の記事が何か参考になれば幸いです