N+1問題とは?
SQLが必要以上に実行されてしまいパフォーマンスが落ちる問題のことです。 それぞれのモデルに、
has_many :boards
belongs_to :user
といった定義を施すことで、関係性を定義することができます。
N+1問題はアソシエーション有りの時に生じる問題であり、上記の例で言うと掲示板の一覧を表示する際にユーザーの名前を取得するために掲示板の数だけSQLが発行されてしまいます。
具体的には次のようなコードになります。
class BoardsController < ApplicationController
def index
@boards = Board.all
end
end
<% @boards.each do |board| %>
<%= render partial: 'board', locals: { board: board } %>
<% end %>
上記の書き方では「Boardモデルから掲示板一覧情報」を1回取得し、「紐ついたuser_idをuserの数だけ」n回取得しています。 そしてview画面でパーシャルを呼び出し、@boardsをeach文によって繰り返し表示しています。
この方法でも表示はされますが、SQLが多く発行され、遅くなったりパフォーマンスが下がるんですね。
includesメソッドで関連付けを一括読み込みをする
includesは、関連付いたモデルのデータを先に取得するメソッドです。このメソッドを使うことで、Boardモデルからデータを取得する際に、関連するUserモデルのデータもまとめて取得してくれます。
class BoardsController < ApplocationController
def index
@boards = Board.all.includes(:user).order(created_at: :desc)
end
end
部分テンプレートを繰り返しrenderにする
上記のコードではパーシャルを呼び出し、each文によって繰り返し表示されていました。 そこを下記のように変更します。
<%= render @boards %>
だいぶスッキリしましたね。 これは@boards
というふうに複数形にすることによって、Railsが「_board.html.erb
」を自動的に探してくれています。
【コラム】部分テンプレートの省略形について
実は単数形インスタンスを渡す場合と複数形インスタンスを渡す場合では書き方が少し異なります。 それらの書き方を順番に見ていきます。
単数形インスタンス変数を渡す
boards
フォルダ内の_board.html.erb
を呼び出すとします。そして、@board
をテンプレート内で変数board
として使用するとします。
①基本形
<%= render partial: 'boards/board', locals: { board: @board } %>
「partial:
」以下でファイル名を指定し、「locals:
」以下の記述で、@board
を部分テンプレート内で変数board
として使用しています。 board = @board
ということですね。
②省略形パターン1
<%= render 'boards/board', board: @board %>
単に「partial」と「locals」を省略しただけの形となります。
③省略形パターン2
<%= render @board %>
インスタンス変数とファイル名が同じ場合、上記のような省略形にすることができます。 かなりスマートな記述になりました。
複数形のインスタンスを渡す
boards
フォルダ内の_board.html.erb
を呼び出すとします。そして、@boards
をテンプレート内で変数board
として使用するとします。
①省略形パターン1
<%= render partial: 'boards/board', collection: @boards %>
collectionオプションは、渡されたインスタンスの要素の分だけそのテンプレートを繰り返し表示する事ができるオプションです。 つまりコレクションの中のインスタンスが部分テンプレートに呼ばれる変数となるため、each文を使用せずに繰り返し処理ができます。
②省略形パターン2
<%= render @boards %>
ファイル名と複数形sを除いたインスタンス名が一致しており、かつeach文のように繰り返し表示したい場合は、上記のように省略することができます。 今回の場合だと_board.html.erb
と@boards
なので、一致しているということになります。
さらに表現を変えると、
<%= render @boards %>
という省略形は、
<% @boards.each do |board| %>
<%= render partial: 'board', locals: { board: board } %>
<% end %>
上記の形に書き換える事もできます。
少しややこしいと思いますが、renderの書き方にはいろいろあるので整理してみてください。
参考記事

コメント