[英]Rails app using STI — easiest way to pull these records?
我正在学习有关Rails的方法,并且正在开发一个示例应用程序来跟踪啤酒食谱。
我有一个名为Recipe的模型,其中包含食谱名称和效率。
我有一个名为STI的模型,该模型使用STI-分为麦芽,啤酒花和酵母。
最后,要链接食谱和配料,我使用了一个称为rec_items的联接表,该表保存了recipe_id,gredit_id和该食谱/配料组合所特有的信息,例如数量和煮沸时间。
一切似乎都运行良好-我可以使用Malt.all找到所有的麦芽,也可以使用Ingredients.all找到所有配料。 我可以使用@ recipe.ingredients等找到配方的成分...
但是,我现在正在处理食谱的显示视图,并且对实现以下目标的最佳方法感到困惑:
我想显示配方名称和相关信息,然后列出成分,但按成分类型分开。 因此,如果我的黑IPA效率为85%,并且它具有5个麦芽和3个啤酒花品种,则输出将类似于:
BLACK IPA (85%)
Ingredient List
MALTS:
malt 1
malt 2
...
HOPS:
hop 1
...
现在,我可以拉@ recipe.rec_items并对其进行遍历,测试类型==“ Malt”的每个rec_item.ingredient,然后对啤酒花进行相同的操作,但这似乎不是Rails-y也不高效。 那么最好的方法是什么? 我可以使用@ recipe.ingredients.all提取所有成分,但不能使用@ recipe.malts.all或@ recipe.hops.all提取那些类型。
我应该使用其他语法吗? 我应该使用@ recipe.ingredient.find_by_type(“ Malt”)吗? 在控制器中执行此操作并将集合传递给视图,还是在视图中正确执行? 我还需要在我的Hop和Malt模型中指定has_many关系吗?
我可以使用条件语句或find_by_type使它按我想要的方式工作,但是我的重点是使用尽可能少的数据库开销来执行这种“ Rails方式”。
谢谢您的帮助!
当前的基本代码:
Recipe.rb
class Recipe < ActiveRecord::Base
has_many :rec_items
has_many :ingredients, :through => :rec_items
end
Ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :rec_items
has_many :recipes, :through => :rec_items
end
Malt.rb
class Malt < Ingredient
end
Hop.rb
class Hop < Ingredient
end
RecItem.rb
class RecItem < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
recipes_controller.rb
class RecipesController < ApplicationController
def show
@recipe = Recipe.find(params[:id])
end
def index
@recipes = Recipe.all
end
end
更新以添加
我现在无法访问联接表属性,所以我发布了一个新问题:
Rails-使用group_by和has_many:through并尝试访问联接表属性
如果有人可以帮助您,我将不胜感激!
自从我使用STI以来已经有一段时间了,被烧了一两次。 因此,我可能会跳过一些STI-fu,这会使此操作变得更容易。 那个...
有很多方法可以做到这一点。 首先,您可以为麦芽,啤酒花和酵母中的每一种创造一个范围。
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
这将允许您执行以下操作:
malts = @recipe.ingredients.malt
hops = @recipe.ingedients.hops
尽管这很方便,但从数据库角度来看,这并不是最有效的方法。 我们必须执行三个查询才能获得所有三种类型。
因此,如果我们不想在每个食谱中谈论很多配料,最好将所有@ recipe.ingredients放入其中,然后将它们分组,例如:
ingredients = @recipe.ingredients.group_by(&:type)
这将执行一个查询,然后将它们分组为ruby内存中的哈希。 哈希将被键入类型,看起来像:
{"Malt" => [first_malt, second_malt],
"Hops" => [first_hops],
"Yeast" => [etc]
}
然后,您可以参考该集合以显示所需的项目。
ingredients["Malt"].each {|malt| malt.foo }
您可以在此处使用group_by
。
recipe.ingredients.group_by {|i| i.type}.each do |type, ingredients|
puts type
ingredients.each do |ingredient|
puts ingredient.inspect
end
end
在这种情况下,STI的效用值得怀疑。 进行简单的分类可能会更好:
class Ingredient < ActiveRecord::Base
belongs_to :ingredient_type
has_many :rec_items
has_many :recipes, :through => :rec_items
end
IngredientType定义您的各种类型,并从该点开始一直是数字常量。
当您尝试显示列表时,这变得更加容易。 我通常更喜欢直接提取中间记录,然后根据需要加入:
RecItem.sort('recipe_id, ingredient_type_id').includes(:recipe, :ingredient).all
这样,您便可以根据需要灵活地进行排序和分组。 您可以调整排序条件以获得正确的顺序。 如果您在类型列上进行排序,这也可能与STI一起使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.