はじめに
RailsアプリケーションにおいてDeviseを用いたユーザー認証機能のカスタマイズ中にActiveRecord::RecordNotFound - Couldn't find Staff without an ID:
というエラーメッセージが発生しました。この記事では、このエラーがなぜ発生したのか、そしてどのようにして解決に至ったのかを共有します。
エラーの内容
ユーザー情報の更新フォームを送信した際に、以下のエラーメッセージが表示されました。
ActiveRecord::RecordNotFound - Couldn't find Staff without an ID:
app/controllers/staffs/registrations_controller.rb:46:in `set_staff'
Started GET "/staffs/edit?id=2" for ::1 at 2024-02-28 00:00:38 +0900
Processing by Staffs::RegistrationsController#edit as HTML
Parameters: {"id"=>"2"}
Staff Load (1.8ms) SELECT "staffs".* FROM "staffs" WHERE "staffs"."id" = $1 ORDER BY "staffs"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
Staff Load (1.9ms) SELECT "staffs".* FROM "staffs" WHERE "staffs"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]]
↳ app/controllers/staffs/registrations_controller.rb:46:in `set_staff'
このメッセージは、一般的には、IDを指定せずにStaffオブジェクトを検索しようとしたときに発生します。
しかし、ログを確認するとidはパラメータとしてきちんと送信されています。問題はset_staffメソッド内で発生しているようですが、そのメソッドが正しくIDを使用してStaffオブジェクトを検索していることがログからも確認できます。
原因と経緯
エラーの根本的な原因は、set_staff
メソッドとそのbefore_action
コールバックがDeviseのコントローラー内に記述されてDeviseの内部処理と競合していたことにありました。これらのコードは、@staff
オブジェクトを設定するために意図されていましたが、編集画面で@staff
変数が適切にセットされていない状況を引き起こしていました。
解決策
set_staff
メソッドとそれを呼び出すbefore_action
コールバックの削除が解決策でした。これらのコード行を削除することで、Deviseのデフォルトの挙動に任せることができ、エラーは解消されました。
before_action :set_staff, only: %i(edit update destroy)
def set_staff
@staff = Staff.find(params[:id])
end
なお、もしset_staff
を使用したい場合は
def set_staff
@staff = Staff.find(resource.id)
end
とすると使用できます。
まとめ
この問題は、Deviseのフレームワークの内部動作に十分な理解がない状態で余計なコードを追加してしまった結果生じました。Deviseのような複雑なライブラリを使用する際には、そのデフォルトの動作を理解し、必要なカスタマイズのみを慎重に行うことが重要です。