使用has_many:through时如何访问rails连接模型属性

时间:2009-03-12 10:13:13

标签: ruby-on-rails ruby activerecord

我有一个像这样的数据模型:

# columns include collection_item_id, collection_id, item_id, position, etc
class CollectionItem < ActiveRecord::Base
  self.primary_key = 'collection_item_id'
  belongs_to :collection
  belongs_to :item
end

class Item < ActiveRecord::Base
  has_many :collection_items
  has_many :collections, :through => :collection_items, :source => :collection
end

class Collection < ActiveRecord::Base
  has_many :collection_items, :order => :position
  has_many :items, :through => :collection_items, :source => :item, :order => :position
end

一个Item可以出现在多个集合中,也可以在不同位置的同一个集合中出现多次。

我正在尝试创建一个帮助器方法,该方法创建一个菜单,其中包含每个集合中的每个项目。我想使用collection_item_id来跟踪请求之间当前选择的项目,但我无法通过Item类访问连接模型的任何属性。

def helper_method( collection_id )
  colls = Collection.find :all
  colls.each do |coll|
    coll.items.each do |item|
# !!! FAILS HERE ( undefined method `collection_item_id' )
      do_something_with( item.collection_item_id )
    end
  end
end

我也尝试了这个,但它也失败了(未定义的方法`collection_item')

do_something_with( item.collection_item.collection_item_id )

编辑:感谢serioys sam指出上面显然是错误的

我还尝试访问连接模型中的其他属性,如下所示:

do_something_with( item.position )

do_something_with( item.collection_item.position )

编辑:感谢serioys sam指出上面显然是错误的

但他们也失败了。

有人可以建议我如何处理这个问题吗?

修改:--------------------&gt;

我从在线文档中发现,使用has_and_belongs_to_many会将连接表属性附加到已检索的项目,但显然已弃用。我还没有尝试过。

目前我正在修改我的Collection模型,如下所示:

class Collection < ActiveRecord::Base
  has_many :collection_items, :order => :position, :include => :item
  ...
end

并更改帮助程序以使用coll.collection_items而不是coll.items

修改:--------------------&gt;

我已经改变了我的帮手,如上所述工作并且工作正常 - (谢谢你)

这使我的代码变得一团糟 - 因为其他因素在这里没有详细说明 - 但是重新分解的一两个小时都不会解决。

3 个答案:

答案 0 :(得分:3)

在您的示例中,您已在Item模型关系中为collection_items和collections定义了has_many,生成的关联方法分别是collection_items和collections,它们都返回一个数组,因此您尝试访问此处的方式是错误的。这主要是对很多人的关系。只需查看此Asscociation Documentation以获取进一步参考。

答案 1 :(得分:3)

do_something_with( item.collection_item_id )

此操作失败,因为item没有collection_item_id成员。

do_something_with( item.collection_item.collection_item_id )

此操作失败,因为item没有collection_item成员。

请记住item和collection_items之间的关系是has_many。因此,item具有collection_items,而不仅仅是单个项目。此外,每个集合都有一个集合项列表。你想做的可能就是:

colls = Collection.find :all
colls.each do |coll|
  coll.collection_items.each do |collection_item|
    do_something_with( collection_item.id )
  end
end

其他一些建议:

  • 您是否阅读过Rails指南中的documentation for has_many :through?非常好。
  • 您不应该在has_many声明中使用:source参数,因为您已经以合理的方式命名了模型和关联。
  

我从在线文档中发现,使用has_and_belongs_to_many会将连接表属性附加到已检索的项目,但显然已弃用。我还没有尝试过。

我建议你坚持使用has_many:through,因为has_and_belongs_to_many更令人困惑,并没有提供任何真正的好处。

答案 2 :(得分:0)

我能够为我的一个模特工作:

class Group < ActiveRecord::Base
  has_many :users, :through => :memberships, :source => :user do
    def with_join
      proxy_target.map do |user|
        proxy_owner = proxy_owner()
        user.metaclass.send(:define_method, :membership) do
          memberships.detect {|_| _.group == proxy_owner}
        end
        user
      end
    end
  end
end

在你的情况下,这样的事情应该有效(未经测试):

class Collection < ActiveRecord::Base
  has_many :collection_items, :order => :position
  has_many :items, :through => :collection_items, :source => :item, :order => :position do
    def with_join
      proxy_target.map do |items|
        proxy_owner = proxy_owner()
        item.metaclass.send(:define_method, :join) do
          collection_items.detect {|_| _.collection == proxy_owner}
        end
        item
      end
    end
  end
end

现在只要您访问这样的项目(items.with_join),就可以从项目中访问CollectionItem:

def helper_method( collection_id )
  colls = Collection.find :all
  colls.each do |coll|
    coll.items.with_join.each do |item|
      do_something_with( item.join.collection_item_id )
    end
  end
end

这是一个更通用的解决方案,您可以使用它将此行为添加到任何has_many :through关联: http://github.com/TylerRick/has_many_through_with_join_model

class Collection < ActiveRecord::Base
  has_many :collection_items, :order => :position
  has_many :items, :through => :collection_items, :source => :item, :order => :position, :extend => WithJoinModel
end