前提・実現したいこと
独学でRailsを学び始めて2ヶ月目の初学者です。
半日試行錯誤したものの、どうしても分からず初投稿します。
現在、Ruby on Railsでお酒と料理の組み合わせ(ペアリング)を投稿・評価するアプリを作っています。
環境:Ruby 2.4.1/ Rails 5.2.1
発生している問題
VIEW(トップページ)に「お酒の名前」「料理名」「ユーザーの評価の平均値(星マーク)」をeach処理で表示しているのですが、ページの読み込みにかなりの時間がかかっています。
ログを確認すると大量のSQLクエリが発行されており、いろいろと調べた結果、N+1問題が発生していると判明しました。大量発生しているSQL文は、Pairモデルに記述しているevaluetion_average内で、全ユーザーの評価の平均を算出する為に使っているsumとcountでした。
いろいろと検索して調べた結果、preloadやeager_loadを使えばいいというところまでは分かりましたが(合っているか自信がありませんが)、解説をしてくれているサイトの使用例と違い、自分の場合はActiveRecordが入れ子になっているわけでもなく、どこに挟めばいいのかがわからずにいます。
そもそもの問題は、そもそもモデルの関連付けをうまく利用できていないからにも思えますが・・・
どこを改善すればN+1問題が解決するか、ご教授いただければと思います。
該当のソースコード
▼model
【sake.rb】 カラム: :name, :kind
class Sake < ApplicationRecord has_many :pairs has_many :foods, through: :pairs end
【food.rb】 カラム: :name
class Food < ApplicationRecord has_many :pairs has_many :sakes, through: :pairs end
【pair.rb】 カラム: :sake_id, :food_id, :user_id
class Pair < ApplicationRecord belongs_to :sake belongs_to :food belongs_to :user, optional: true has_many :evaluations # ここのメソッド内のActiveRecordが大量のSQLを生み出していました。 def evaluation_average begin average = Evaluation.where(pair_id: self.id).sum(:point) / Evaluation.where(pair_id: self.id).count.to_f return average rescue average = 0 end end end
【evaluation.rb】 カラム: :sake_id, :food_id, :user_id
class Evaluation < ApplicationRecord belongs_to :user belongs_to :pair end
▼Controller
PairsController
1class PairsController < ApplicationController 2 3 def index 4 @pairs = Pair.all.includes(:sake, :food) 5 end 6 〜省略〜 7end
▼View
<% @pairs.each do |pair| %> <p><%=link_to "#{pair.sake.name} (#{pair.sake.kind}) × #{pair.food.name}", pair_path(pair) %></p> <span style="--rate: <%= pair.evaluation_average*20.0 %>%;" class="evaluation-star"></span> <span class="evaluation-number"><%= pair.evaluation_average.to_f.round(1) %></span> <% end %>
お手数ですが、
ご回答のほど、よろしくお願い致します。
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/11/26 02:21
2018/11/26 02:31
2018/11/26 03:11
2018/11/26 03:23
2018/11/26 03:56
2018/11/26 04:28