检测到邮箱程序 N+1 查询
Mailboxer N+1 Query detected
我设法用 this tutorial 安装了 Mailboxer,但我有一个反复出现的错误:
N+1 Query detected
Mailboxer::Message => [:sender]
Add to your finder: :includes => [:sender]
N+1 Query method call stack
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
我有另一个 :message,但我用 includes 解决了这个问题。如果我尝试对 :sender 做同样的事情,我会遇到这个错误:
Association named 'sender' was not found on Mailboxer::Receipt; perhaps you misspelled it?
教程原码:conversations_controller.rb
class ConversationsController < ApplicationController
before_action :authenticate_user!
def new
end
def create
recipients = User.where(id: conversation_params[:recipients])
conversation = current_user.send_message(recipients, conversation_params[:body], conversation_params[:subject]).conversation
flash[:success] = "Your message was successfully sent!"
redirect_to conversation_path(conversation)
end
def show
@receipts = conversation.receipts_for(current_user)
# mark conversation as read
conversation.mark_as_read(current_user)
end
def reply
current_user.reply_to_conversation(conversation, message_params[:body])
flash[:notice] = "Your reply message was successfully sent!"
redirect_to conversation_path(conversation)
end
def trash
conversation.move_to_trash(current_user)
redirect_to mailbox_inbox_path
end
def untrash
conversation.untrash(current_user)
redirect_to mailbox_inbox_path
end
private
def conversation_params
params.require(:conversation).permit(:subject, :body,recipients:[])
end
def message_params
params.require(:message).permit(:body, :subject)
end
end
教程原码:show.html.erb
<div class="row">
<div class="spacer"></div>
<div class="col-md-6">
<%= link_to "Compose", new_conversation_path, class: "btn btn-success" %>
</div>
<div class="col-md-6 text-right">
<% if conversation.is_trashed?(current_user) %>
<%= link_to 'Untrash', untrash_conversation_path(conversation), class: 'btn btn-info', method: :post %>
<% else %>
<%= link_to 'Move to trash', trash_conversation_path(conversation), class: 'btn btn-danger', method: :post,
data: {confirm: 'Are you sure?'} %>
<% end %>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<%= render 'mailbox/folders' %>
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<%= render partial: 'messages' %>
</div>
<div class="panel-footer">
<!-- Reply Form -->
<%= form_for :message, url: reply_conversation_path(conversation) do |f| %>
<div class="form-group">
<%= f.text_area :body, placeholder: "Reply Message", rows: 4, class: "form-control" %>
</div>
<%= f.submit "Reply", class: 'btn btn-danger pull-right' %>
<% end %>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
教程原码:_messages.html.erb
<% @receipts.each do |receipt| %>
<% message = receipt.message %>
<div class="media">
<div class="media-left">
<!-- user avators can go here -->
<a href="#">
<img class="media-object" src="http://placehold.it/64x64" alt="...">
</a>
</div>
<div class="media-body">
<h4 class="media-heading">
<%= message.sender.username %> <br>
<small><b>Subject: </b><%= message.subject %></small><br>
<small><b>Date: </b><%= message.created_at.strftime("%A, %b %d, %Y at %I:%M%p") %></small>
</h4>
<%= message.body %>
</div>
</div>
<% end %>
如果我删除 <%= message.sender.username %> 问题就解决了...
有什么想法吗?
通常,为了修复这些 N+1 query problems,我们使用 includes
并通过传递散列,我们可以 includes
嵌套关联。所以看看这段代码,调用 receipt.message.sender
会触发错误,所以我们有一个 Receipt
模型,一个 message
关联和一个 sender
与之关联。因此,如果我们能找到加载 Receipt
的位置,我们就可以像对任何其他模型一样添加 includes(message: :user)
。
深入研究 mailboxer gem,show
操作中的 receipts_for
方法只是 Mailboxer::Receipt
上几个作用域的包装器。因为这个方法只是为你运行一些范围,我们可以链接到它的末尾,就好像它是一个普通的 ActiveRecord
where
链。
考虑到所有这些,我们应该能够添加 includes
并避免 N+1 查询问题,最终得到类似
的结果
@receipts = conversation.receipts_for(current_user).includes(message: :sender)
我设法用 this tutorial 安装了 Mailboxer,但我有一个反复出现的错误:
N+1 Query detected
Mailboxer::Message => [:sender]
Add to your finder: :includes => [:sender]
N+1 Query method call stack
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
app/views/conversations/_messages.html.erb:12:in `block in _app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/_messages.html.erb:1:in `_app_views_conversations__messages_html_erb__3829465222244059655_69982701059040'
app/views/conversations/show.html.erb:27:in `_app_views_conversations_show_html_erb__1439517360344897040_69982700516580'
我有另一个 :message,但我用 includes 解决了这个问题。如果我尝试对 :sender 做同样的事情,我会遇到这个错误:
Association named 'sender' was not found on Mailboxer::Receipt; perhaps you misspelled it?
教程原码:conversations_controller.rb
class ConversationsController < ApplicationController
before_action :authenticate_user!
def new
end
def create
recipients = User.where(id: conversation_params[:recipients])
conversation = current_user.send_message(recipients, conversation_params[:body], conversation_params[:subject]).conversation
flash[:success] = "Your message was successfully sent!"
redirect_to conversation_path(conversation)
end
def show
@receipts = conversation.receipts_for(current_user)
# mark conversation as read
conversation.mark_as_read(current_user)
end
def reply
current_user.reply_to_conversation(conversation, message_params[:body])
flash[:notice] = "Your reply message was successfully sent!"
redirect_to conversation_path(conversation)
end
def trash
conversation.move_to_trash(current_user)
redirect_to mailbox_inbox_path
end
def untrash
conversation.untrash(current_user)
redirect_to mailbox_inbox_path
end
private
def conversation_params
params.require(:conversation).permit(:subject, :body,recipients:[])
end
def message_params
params.require(:message).permit(:body, :subject)
end
end
教程原码:show.html.erb
<div class="row">
<div class="spacer"></div>
<div class="col-md-6">
<%= link_to "Compose", new_conversation_path, class: "btn btn-success" %>
</div>
<div class="col-md-6 text-right">
<% if conversation.is_trashed?(current_user) %>
<%= link_to 'Untrash', untrash_conversation_path(conversation), class: 'btn btn-info', method: :post %>
<% else %>
<%= link_to 'Move to trash', trash_conversation_path(conversation), class: 'btn btn-danger', method: :post,
data: {confirm: 'Are you sure?'} %>
<% end %>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<%= render 'mailbox/folders' %>
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<%= render partial: 'messages' %>
</div>
<div class="panel-footer">
<!-- Reply Form -->
<%= form_for :message, url: reply_conversation_path(conversation) do |f| %>
<div class="form-group">
<%= f.text_area :body, placeholder: "Reply Message", rows: 4, class: "form-control" %>
</div>
<%= f.submit "Reply", class: 'btn btn-danger pull-right' %>
<% end %>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
教程原码:_messages.html.erb
<% @receipts.each do |receipt| %>
<% message = receipt.message %>
<div class="media">
<div class="media-left">
<!-- user avators can go here -->
<a href="#">
<img class="media-object" src="http://placehold.it/64x64" alt="...">
</a>
</div>
<div class="media-body">
<h4 class="media-heading">
<%= message.sender.username %> <br>
<small><b>Subject: </b><%= message.subject %></small><br>
<small><b>Date: </b><%= message.created_at.strftime("%A, %b %d, %Y at %I:%M%p") %></small>
</h4>
<%= message.body %>
</div>
</div>
<% end %>
如果我删除 <%= message.sender.username %> 问题就解决了...
有什么想法吗?
通常,为了修复这些 N+1 query problems,我们使用 includes
并通过传递散列,我们可以 includes
嵌套关联。所以看看这段代码,调用 receipt.message.sender
会触发错误,所以我们有一个 Receipt
模型,一个 message
关联和一个 sender
与之关联。因此,如果我们能找到加载 Receipt
的位置,我们就可以像对任何其他模型一样添加 includes(message: :user)
。
深入研究 mailboxer gem,show
操作中的 receipts_for
方法只是 Mailboxer::Receipt
上几个作用域的包装器。因为这个方法只是为你运行一些范围,我们可以链接到它的末尾,就好像它是一个普通的 ActiveRecord
where
链。
考虑到所有这些,我们应该能够添加 includes
并避免 N+1 查询问题,最终得到类似
@receipts = conversation.receipts_for(current_user).includes(message: :sender)