「現場で使えるRuby on Rails5速習実践ガイド」を進めていて、P.169の マイグレーションを実行すると以下のエラーが発生しました。
/taskleaf:$ bin/rails db:migrate
== 20210524174653 AddUserIdToTasks: migrating =================================
-- execute("DELETE FROM tasks;")
-> 0.0009s
-- add_reference(:tasks, :user, {:null=>false, :index=>true})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: Cannot add a NOT NULL column with default value NULL: ALTER TABLE "tasks" ADD "user_id" integer NOT NULL
...
(省略)
試したこと
どのマイグレーションファイルまで実行されたかを確認したくて調べたところ、以下のコマンドで確認できそうでした。
bundle exec rake db:migrate:status
実行すると
/taskleaf:$ bundle exec rake db:migrate:status
database: /Users/{ユーザID}/workspace/runteq/genba_rails/taskleaf/db/development.sqlite3
Status Migration ID Migration Name
--------------------------------------------------
up 20210517160153 Create tasks
up 20210521153755 Change task name not null
up 20210521160018 Change tasks name limit30
up 20210521172340 Create users
up 20210521175601 Add admin to users
down 20210524174653 Add user id to tasks
どうやら直近(P.169)で作成したマイグレーションファイルが実行できず、ロールバックされていそうです。
エラーログに戻ると、
SQLite3::SQLException: Cannot add a NOT NULL column with default value NULL: ALTER TABLE "tasks" ADD "user_id" integer NOT NULL
英語が目にくるのでDeepL翻訳にかけたところ
SQLite3::SQLException: デフォルト値がNULLのNOT NULLカラムを追加できません: ALTER TABLE "Tasks" ADD "user_id" integer NOT NULL
らしいです。
原因
書籍ではDBにPostgreSQLを指定してアプリを立ち上げていましたが、SQLiteで進めていたことが原因のようです。
当然である.NOT NULL な column なのに default を指定しなければ,もし行が既に存在した場合その
column (今回は price) が NULL にならざるを得ないではないか.しかし PostgreSQL だとこれが通るのである.先のコマンドを以下のように変えて見ると分かる.
解消した方法
マイグレーションファイルを以下のように修正しました。
add_referenceを実行したあと
後からnot null制約をつける(change_column_null)ことで正常にマイグレーションが実行できました。
(コメントアウト行がエラー発生した書籍どおりのコードです。)
class AddUserIdToTasks < ActiveRecord::Migration[5.2]
def up
execute 'DELETE FROM tasks;'
# add_reference :tasks, :user, null: false, index: true
add_reference :tasks, :user, index: true
change_column_null :tasks, :user_id, false
end
def down
remove_reference :tasks, :user, index: true
end
end