I’m probably overlooking a simple approach here, but what is the best way to code a table that is dynamic in 2 axes. I’m using Rails 3.2
For example, say I have the following models
class Region
has_many :shops
end
class Shop
has_many :sales
has_many :products, through: :sales
end
class Sale
belongs_to :shop
belongs_to :product
end
class Product
has_many :sales
has_many :shops, through: :sales
end
In the Region show view I want to display a table that lists Shops at column headers, Products as row headers, and calculates the average Sale.price at each cell.
I am getting into a confused mess of nested blocks, and the calculated values don’t appear to correspond with what I’d expect.
Is there a simple, Rails-y way to do something like this? Or can anyone recommend any example code I could study.
My actual models are a little more complex than I’ve described enough, and I’m wondering whether I should spend the time working through my code to debug, or whether I should be following a simpler approach. This seems like a reasonably common requirement.
EDIT
An example of my code
#views/regions/show.html.erb
<table>
<tr>
<th>Shop Name</th>
<% for product in @region.products.uniq %>
<th><%= product.name%></th>
<% end %>
</tr>
<% for shop in @region.shops.uniq %>
<tr>
<td><%= shop.name %></td>
<% for product in @region.products.uniq %>
<td><%= product.sales.average(:price, conditions: ['sale.shop_id = ?', shop], :include => :sale) %></td>
<% end %>
</tr>
<% end %>
</table>
You can try this to calculate the average sales for each shop.
Shop.joins(:sales).average(:price, group: :product_id)This will result with a hash where the keys are the product ids and the values are the average for that product. If you want to show more details than just the product id, you can change the
:groupoption to something like (using postgre)Shop.joins(sales: :product).average(:price, group: "product_id || ' - ' || products.name")UPDATE: using the view template on the question (warning, this will be slow on a big set of records)
UPDATE: this may be a faster way to do it