模型关联返回的数组不是数组?

时间:2011-03-11 22:03:44

标签: ruby-on-rails ruby associations

我们有一个看起来像这样的模型关联:

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank'
end

rank列是整数类型。这些特定模型的细节并不重要,因为我们发现其他模型之间的其他has_many关联存在同样的问题。

我们还添加了Enumerable模块:

module Enumerable
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'
    method = name.to_s[8..-1]
    collect{|element| element.send(method)}
  end
end

这会添加一个collect_id方法,我们可以使用它从ActiveRecord对象数组中获取记录ID数组。

因此,如果我们使用普通的ActiveRecord find:all,我们得到一个很好的数组,然后我们可以使用collect_id,但是如果我们使用Example.others.collect_id,我们得到

NoMethodError: undefined method `collect_id' for #<Class:0x2aaaac0060a0>

Example.others.class返回“数组”,所以是说谎还是混淆?

到目前为止,我们的解决方案是以这种方式使用它:

Example.others.to_a.collect_id 

这有效,但这看起来有点奇怪。你为什么要那样做?

我们使用的是Ruby 1.8.7和Rails 2.3.4

4 个答案:

答案 0 :(得分:5)

模型关联是代理,而不仅仅是简单的数组。

我建议您使用example.others.all.collect_id而不是example.others.all.map(&:id)和补丁,这是标准的Rails和Ruby&gt; = 1.8.7方式来收集单个属性。

答案 1 :(得分:3)

由于性能原因,ActiveRecord关联懒洋洋地加载has_many记录。例如,如果您调用example.others.count,则无需加载所有记录。尝试将此补丁与您的补丁一起添加到可枚举:

class ActiveRecord::Associations::AssociationCollection
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'

    load_target unless loaded?
    method = name.to_s[8..-1]
    @target.collect{|element| element.send(method)}
  end
end

答案 2 :(得分:2)

两种可能的解决方案:

1)扩展特定关联:

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank' do
    def method_missing(name)
      super unless name.to_s[0..7] == 'collect_'
      method = name.to_s[8..-1]
      collect{|element| element.send(method)}
    end
  end
end

2)将扩展添加到模块以获得可重复的解决方案。

Rails提供了扩展关联数组的选项。

module Collector
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'
    method = name.to_s[8..-1]
    collect{|element| element.send(method)}
  end
end

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank', :extend => Collector
end

阅读documentation了解更多详情。在页面中搜索“Association Extensions”以进入相关部分。

答案 3 :(得分:0)

您应该使用all

 example.others.all.collect_id