On Rails 4.我最近为我的开发环境安装了bullet gem来清理我的应用程序的N + 1查询。相关模型:
提交:属于类别和用户。有很多SubmissionDetails。
用户:有多个提交
类别:有很多提交内容。属于奖项。
奖励:包含多个类别(以及按类别提交的内容)
提交详细信息:属于提交内容
在我提交的索引页面中,我有一个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]
(因此,我还需要在声明中加入一种方法,使奖励适合那里,再次,通过类别提交了许多提交)。
所以假设我不能为三个不同的模型做一个.includes
,还有另一种方法可以解决这个问题吗?感谢。
答案 0 :(得分:7)
为了更清楚,让我让细节更加明显:
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,您将列出他的提交内容。 对于每个提交,您要列出submission_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 :(得分: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对此类引用并不太聪明。