Rails app using STI — easiest way to pull these records?

a 夏天 提交于 2019-12-06 14:18:19

It's been a while since I've used STI, having been burned a time or two. So I may be skipping over some STI-fu that would make this easier. That said...

There are many ways of doing this. First, you could make a scope for each of malt, hops, and yeast.

class Ingredient < ActiveRecord::Base
  has_many :rec_items
  has_many :recipes, :through => :rec_items
  named_scope :malt, :conditions =>  {:type => 'Malt'}
  named_scope :hops, :conditions => {:type => 'Hops'}
  ...
end

This will allow you to do something line:

malts = @recipe.ingredients.malt
hops = @recipe.ingedients.hops

While this is convenient, it isn't the most efficient thing to do, database-wise. We'd have to do three queries to get all three types.

So if we're not talking a ton of ingredients per recipe, it'll probably be better to just pull in all @recipe.ingredients, then group them with something like:

ingredients = @recipe.ingredients.group_by(&:type)

This will perform one query and then group them into a hash in ruby memory. The hash will be keyed off of type and look something like:

{"Malt" => [first_malt, second_malt],
 "Hops" => [first_hops],
 "Yeast" => [etc]
}

You can then refer to that collection to display the items however you wish.

ingredients["Malt"].each {|malt| malt.foo }

You can use group_by here.

recipe.ingredients.group_by {|i| i.type}.each do |type, ingredients|
  puts type

  ingredients.each do |ingredient|
    puts ingredient.inspect
  end
end

The utility of STI in this instance is dubious. You might be better off with a straight-forward categorization:

class Ingredient < ActiveRecord::Base
  belongs_to :ingredient_type

  has_many :rec_items
  has_many :recipes, :through => :rec_items
end

The IngredientType defines your various types and ends up being a numerical constant from that point forward.

When you're trying to display a list this becomes easier. I usually prefer to pull out the intermediate records directly, then join out as required:

RecItem.sort('recipe_id, ingredient_type_id').includes(:recipe, :ingredient).all

Something like that gives you the flexibility to sort and group as required. You can adjust the sort conditions to get the right ordering. This might also work with STI if you sort on the type column.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!