Madogiwa Blog

主に技術系の学習メモに使っていきます。

RubyonRails:current_userへのメソッド呼び出しで発生したN+1に対応する

まどぎわです(・∀・)今日は、current_userへのN+1問題の対応についてです!
railsで開発していると、N+1問題にぶつかって対応することが多いかと思います。 普通であれば、DBアクセス時にincludes等を使って関連モデルをキャッシュしておくことで対応することが多いかと思います。

しかし、current_userのメソッド呼び出しでN+1問題が発生してしまった場合、だいたいDBアクセス処理はライブラリで行われているため、DBアクセス時に関連モデルをキャッシュしようとしても、「どうすれば、いいんや・・・」という気持ちになり、対応方法を調べたのでメモしておきますφ(..)

前提

今回は、下記のようなUserモデルとQuestionモデルとQuestionAnswerモデルがあり、Userモデルに#answerd?というUserがその質問に回答済みかどうかを判定するメソッドを定義しています。
何も対応しないとQuiz一覧画面等で、このメソッドを呼び出すとN+1問題が発生してしまいます。。。

class User < ApplicationRecord
  has_many :question_answers

  def answerd?(question)
    question.question_answers.to_a.select{ |answer| answer.user_id == id }.present?
  end
end

class QuestionAnswer < ApplicationRecord
  belongs_to :user
  belongs_to :question
end

class Question < ApplicationRecord
  has_many :question_answers
end

対応方法

current_userをもとにUserインスタンスを再生成する

controller内でcurrent_userをもとにUserインスタンスを再生成し、その際にincludesで関連モデルをキャッシュするようにします!これにより、#answerd?を呼び出してもキャッシュされた値から処理が行われるので、N+1問題を防ぐことが出来ます🙌

class QuizzesController < ApplicationController
  def index
    @user = User.includes(:question_answers).find(current_user.id)
    @questions = Quiz.includes(:choices)
  end
end

参考

stackoverflow.com