如何避免在视图中命中数据库

jam*_*mes 1 ruby model-view-controller ruby-on-rails

我得到的不应该在视图中ping数据库...但是想知道正确的解决方案.在我的一个观点中,我需要根据每个孩子提取一个@order孩子的信息items,以及Amount另一个模型item.像这样的东西:

<% @order.items.each do |item| %>
  <td><%= item.name %></td>
  <td><%= Refund.where(item_id:item.id).first.amount %></td>
  <td><%= Amount.where(item_id: item.id).first.amount %></td>
<% end %>
Run Code Online (Sandbox Code Playgroud)

为了避免视图中的db命中,我想到的唯一解决方案是在控制器中创建所有相关数据的大量哈希值,然后从视图中访问它.所以它会是这样的:

# controller (writing quickly, code may not be totally right, hopefully you get gist
data = Hash.new
data["items"] = []
@order.items.each do |item|
  item_hash = {
    "name" => item.name,
    "amount" => Amount.where(item_id: item.id).first.amount,
    "refund" => Refund.where(item_id:item.id).first.amount
  }
  data["items"] << item_hash
end

# view code
<% data["items"].each do |item| %>
  <td><%= item["name"] %></td>
  <td><%= item["refund"] %></td>
  <td><%= item["amount"] %></td>
<% end %>
Run Code Online (Sandbox Code Playgroud)

我知道很讨厌这类问题......但我真的需要知道......这是最好的解决方案吗?还是有最好的做法?我问的原因是因为它在视图中看起来非常干净,但在控制器中非常笨重,而且当你有一套更复杂的嵌套表时它也变得非常笨重,这就是我实际拥有的(即data哈希)放在一起会非常时髦

Fre*_*ung 5

首先,我将使用item和其他2个类之间的关联,以便您可以这样做

item.refund
item.amount
Run Code Online (Sandbox Code Playgroud)

而不是Refund.where(...).您可以进一步定义诸如的方法

def refund_amount
  refund.amount
end
Run Code Online (Sandbox Code Playgroud)

对于另一个人来说也是如此(并希望得到一个更好的名字而不是amount_amount.

这样可以保持视图和控制器的清洁,但不会更快.到目前为止,所有方法都涉及每个项目运行2个数据库查询,就我而言,这是真正的问题 - 是否在视图中发生了多余的查询,或者控制器的问题较少.

但是,您可以使用Active Record的包含机制来避免这种情况:

Item.include(:amount,:refund).where("your conditions here")
Run Code Online (Sandbox Code Playgroud)

将批量加载命名关联,而不是在访问每个项目时一次加载一个.