障害内容
コントローラのある1つのアクション内でsessionを利用後にデータを削除するような処理を想定して、下記コードを書いたとする
hoge_controller.rb
def action
# イメージ的には検索処理のような感じ
# category_idはInteger型でDBに登録されている
@lists = Book.where(category_id: session[:category_id])
session[:category_id].clear
end
上記のコードではBook
モデルからデータを取得することはできない
実際に発行されているSQLも
SELECT * FROM books WHERE category_id = ''
のような形で出力されることが確認できるはず
通常、Railsで定義される変数類は先行評価の形式をとっているため、
例えwhere検索等の遅延評価系メソッドの後に値をnil
にしても正しくデータを取得することができる
反対に、sessionは基本的に遅延評価であるため遅延評価系メソッドで使用した後にsessionの中身をクリアするとデータの取得ができなくなってしまう
解消方法
今回の例ではwhere検索であるため、それを先行評価メソッドにしてしまえば問題ない
hoge_controller.rb(whereの先行評価ver)
def action
# SQLを直書きする
@lists = Book.where('category_id = :id', id: session[:category_id])
session[:category_id].clear
# take、firstなどで即時発行させる
@lists = Book.where(category_id: session[:category_id]).take
@lists = Book.where(category_id: session[:category_id]).first
session[:category_id].clear
end
# to_aなどで変換を行う(ActiveRecord::Relationクラスではなくなるため要注意)
@lists = Book.where(category_id: session[:category_id]).to_a
session[:category_id].clear
end
また、それとは逆にsessionの値をdup
でコピーするなど、sessionとは別で保持させることでも解消できる
格納させるときにはid = session[:category_id]
のように代入で行わないようにさえすれば大体何とかなる(代入は結局のところメモリ参照のため、参照先が変われば代入された変数の値も変化する)
参考記事
-
ActiveRecord各メソッドのクエリ実行タイミングについて
- ActiveRecordクラスのメソッドについて、詳しい解説が載っています