Rails finder强制加载整个关联?

时间:2012-11-19 22:17:25

标签: ruby-on-rails activerecord

问题类has_many选择。我需要通过一些属性值(通常是id)找到一个选择。

澄清:父记录(问题)是围绕不同的对象和方法传递的,它们使用不同的属性和值搜索子项。

我可以question.choices.find(choice_id),但每次都会生成一个SQL查询。

知道问题通常只有少数选择,并且在一条父记录上会有很多不同的搜索,我怀疑在Ruby中加载整个关联和搜索会更便宜:

question.choices.detect{|choice| choice.id == some_choice_id}

Detect来自Enumerable并强制Rails在第一次调用时加载关联(并在将来的搜索中使用缓存数据)。这也很好地与急切加载:我可以做Question.all(包括:选择),并将整个树加载两个查询,然后搜索非常快。但它读起来并不是很好。

如果我想模拟发现它会变得更加毛茸茸!行为:

question.choices.detect{|choice| choice.id == choice_id} or raise ActiveRecord::RecordNotFound.new

这正是我想要的,我只是在寻找更好的语法。

Rails是否提供了某种行为方式(在第一次查询时强制加载整个关联,然后搜索这个预加载的集合)?

2 个答案:

答案 0 :(得分:0)

我怀疑我错过了在Rails中烘焙或在流行宝石中可用的东西。如果不是这样的话,我只会自己动手帮助:

Enumerable.module_eval do
  def select_by(attribute_name, value)
    detect do |item|
      item.send(attribute_name) == value
    end
  end

  def select_by!(attribute_name, value)
    select_by(attribute_name, value) \
      or raise StandardError.new("can't find item with #{attribute_name} equal to #{value} in #{self.inspect}")
  end
end

用法是:

question.choices.select_by(:id, 7)
user.question.select_by(:name, "Is it shiny?")

答案 1 :(得分:0)

我相信你想要的东西被称为“渴望加载”,并使用.includes方法实现。

@questions = Question.includes(:choices).all

将导致执行内部联接的查询,返回问题及其每个选项。这假设您已在模型中声明了关系。据推测,你将使用where where子句而不是.all来限定。