I have Bouquet and Flower objects:
class Bouquet < ApplicationRecord
has_many :flowers
end
class Flower < ApplicationRecord
belongs_to :bouquet
enum color: {
blue: 0,
red: 1,
yellow: 2,
white: 3
}
end
where a Bouquet will always have exactly one blue flower, one red flower, one yellow flower, and one white flower.
Suppose I have a page where I want to list all bouquets and their flowers in order, how can I query the flowers to minimize page load time?
In view (note that the white flower shows different info than the rest):
<% Bouquet.some_scope.each do |bouquet| %>
<ul>
<li><%= bouquet.name %></li>
<li><%= bouquet.blue_flower.name %></li>
<li><%= bouquet.red_flower.name %></li>
<li><%= bouquet.yellow_flower.name %></li>
<li><%= bouquet.white_flower.name_and_type %></li>
</ul>
<% end %>
- I've tried setting up the Bouquet model as follows:
class Bouquet < ApplicationRecord
has_many :flowers
def blue_flower
flowers.find_by(color: :blue)
end
def red_flower
flowers.find_by(color: :red)
end
# etc...
end
Then in the view:
<% Bouquet.some_scope.includes(:flowers).each do |bouquet| %>
But all the find_bys ignore the includes and query the database for each flower.
- Since the flowers (should) always be created in order, I've also tried
class Bouquet < ApplicationRecord
has_many :flowers
def blue_flower
flowers.first
end
def red_flower
flowers.second
end
# etc...
end
which works, but is it the best way of handling this?
CodePudding user response:
Since the flowers (should) always be created in order, I've also tried
SQL has no inherent order. If you want an order, you must provide it. You could flowers.order(:color)
and then select off the list assuming that #1 is blue, #2 is red, etc... but that's fragile. What if you add more colors?
Since there's only four flowers, turn them into a Hash keyed on the color.
class Bouquet < ApplicationRecord
has_many :flowers
def flower_by_color(color)
flowers_by_color[color.to_s]
end
private def flowers_by_color
@flowers_by_color ||= flowers.index_by { |flower| f.color }
end
end
Rather than hard coding the colors, use Flower.colors
to loop through all the colors. Then if you add more Flower colors it will still work.
<% Bouquet.some_scope.includes(:flowers).each do |bouquet| -%>
<ul>
<li><%= bouquet.name %></li>
<% Flower.colors.sort.each do |color| -%>
<li><%= bouquet.flower_by_color(color).name %></li>
<% end -%>
</ul>
<% end -%>