james
james

Reputation: 4049

How to avoid hitting database in the view

I get that one should not ping the database in the view... but wondering about the right solution. In one of my views, I need to pull info on an @order, it's child items, and also Amount, another model, based on each child item. Something like this:

<% @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 %>

For the sake of avoiding the db hits in the view, the only solution I've thought of is to create a huge hash of all the relevant data in the controller, which is then accessed from the view. So it would be something like this:

# 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 %>

And I know SO hates this type of question... but I really need to know... is that the best solution? Or are there are best practices? The reason I ask is because it seems very clean in the view, but very bulky in the controller, and also it gets quite unwieldy when you have a much more complex set of nested tables, which is what I actually have (i.e., the data hash would be quite funky to put together)

Upvotes: 1

Views: 149

Answers (1)

Frederick Cheung
Frederick Cheung

Reputation: 84114

First of I would use associations between item and the 2 other classes, so that you can do

item.refund
item.amount

Instead of Refund.where(...). You could further define methods such as

def refund_amount
  refund.amount
end

And similarly for the other one (and hopefully come up with a better name than amount_amount.

This keeps both your view and controller clean but it won't be any faster. So far all of the approaches involve running 2 database queries per item which is the real issue as far as I'm concerned - whether those excess queries happen in the view or the controller is of lesser concern.

However you can avoid this with Active Record's include mechanism:

Item.include(:amount,:refund).where("your conditions here")

Will load the named associations in bulk rather than loaded them one at a time as each item is accessed.

Upvotes: 5

Related Questions