6

在 Rails 4 上。我最近为我的开发环境安装了子弹 gem,以清除我的应用程序的 N+1 查询。相关型号:

提交:属于类别和用户。有很多 SubmissionDetails。

用户:有很多提交

类别:有很多提交。属于奖项。

奖项:有许多类别(以及通过类别提交)

SubmissionDetails:属于Submissions

在我提交的索引页面中,我有一个each do声明来显示当前用户所做的每个提交。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>

之后,我列出了有关提交的信息,包括其相关类别、奖项和提交详细信息。Bullet 说我对这个声明有 N+1 个问题:

N+1 Query detected Submission => [:category] Add to your finder: :include => [:category]
N+1 Query detected Submission => [:user] Add to your finder: :include => [:user]
N+1 Query detected Submission => [:submission_details] Add to your finder: :include => [:submission_details]

每次我尝试添加.includes所有这三个模型时,它只会选择我列出的第一个(这并不奇怪)。我想当涉及多个模型时我需要走一条不同的路线——也许是一个加入声明?

(当我将 :category 设为第一项时,它会添加此通知):

N+1 Query detected Category => [:award] Add to your finder: :include => [:award]

(因此,我还需要在声明中包含一种使 Award 也适合其中的方法,该方法同样有许多通过类别提交的内容)。

所以假设我不能.includes为三种不同的模型做一个,还有另一种方法可以解决这个问题吗?谢谢。

4

2 回答 2

9

为了更清楚,让我让细节更明显:

class User < ActiveRecord::Base
  has_many :submissions
end

class Submission < ActiveRecord::Base
  belongs_to :category
  belongs_to :user
  has_many   :submission_details
end

class SubmissionDetail < ActiveRecord::Base
  belongs_to :submission
end

class Category < ActiveRecord::Base
  belongs_to :award
  has_many   :submissions
end

class Award < ActiveRecord::Base
  has_many :categories
  has_many :submissions, through: :categories
end

如果我理解正确,对于您的 current_user,您正在列出他的提交。对于每个提交,您要列出 submit_details 及其所属的类别。对于每个类别,您也列出了奖项。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>
      ...
      <div><%= submission.category %></div>
      <div><%= submission.category.award %></div>
      <%= submission.submissions_details.each do |submission_detail| %>
        ...
      <% end %>
    <% end %>
  </div>
<% end %> 

includes您可以通过以下方式消除 N+1 问题:

current_user.submissions.includes(:submission_details, :category => :award)

更多详情includes请参考:

  1. Rails 引导急切加载关联
  2. Rails 引导快速加载多关联
  3. Rails api - 包括

协会

于 2014-05-20T22:05:06.403 回答
2

为了包括这些关联,我将创建一个提交范围。当您使用它时,添加一个latest范围。

class Submission
  scope :eager,  -> { includes(:submission_details, :category => [:award]) }
  scope :latest, -> { order("created_at DESC") }
end

然后简单

current_user.submissions.latest.eager [...]

您不必包含:user,但我注意到 Rails 对此类引用不太聪明。

于 2014-05-20T22:10:28.583 回答