繁体   English   中英

使用STI的Rails应用程序-获取这些记录的最简单方法?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM