Rails 3 has_many:通过访问属性



我第一次使用has_many through,尽管在这里和指南中读了很多书,但我不理解访问through表上属性的正确方法。我的表格与另一篇文章中的示例相同。

    class Product < ActiveRecord::Base
       has_many :collaborators
       has_many :users, :through => :collaborators
    end
    class User < ActiveRecord::Base
       has_many :collaborators
       has_many :products, :through => :collaborators
   end
   class Collaborator < ActiveRecord::Base
      belongs_to :product
      belongs_to :user
   end

假设合作者表具有额外的属性,比如hours_filed,那么从特定用户和产品的合作者表中查找hours_filted的正确方法是什么?

当我通过产品找到我的用户,并像在中一样对他们进行迭代时

    @product.users.each do |user| 

这似乎工作

    user.collaborator[0].hours_spent

我得到了正确的值,但由于每个用户/产品对应该只有一个合作者记录,所以索引让我很反感,让我觉得自己做错了什么。

感谢您的阅读!

编辑

也许我没有把has_many贯穿概念。也许MySQL的例子会有所帮助。

我当时想的是,如果我做

    SELECT * FROM collaborators where user_id = 1;

我希望结果是一个集合(零或更多)。类似的

    SELECT * FROM collaborators where product_id = 1;

也会给我一套,但

    SELECT * FROM collaborators where user_id = 1 and product_id = 1;

将给出最多1行。

如果我理解正确的话,所有3个查询都返回一个集合。所以我想我需要某种唯一性约束,但这必须是一个复合键,在这两个键上都属于键。这可能吗?有没有一种结构可以更好地模拟这一点?

非常感谢您快速而有用的回复!

每对可能有一个数据库行,但当考虑单个用户时,该用户可以与许多产品相关联,因此用户可以在合作者表中有许多行。类似地,当考虑单个产品时,该产品可以与许多用户相关联,因此一个产品在合作者表中可以有许多行。

此外,如果您只想花费第一个合作者的时间,请使用user.collaborators.first.try(:hours_spent)(可能返回null),而不是使用user.collaborators[0].hours_spent

如果一个用户只能拥有一个产品,而一个产品只能有一个用户,则将所有内容的has_many切换为has_one。

更新:以上是对原始问题的回答,该问题已通过评论进行了澄清。有关详细信息,请参阅评论,并参阅Peter对其他答案的评论

也许您应该使用has_and_belongs_to_many。如果您的协作器仅用于在用户和产品之间建立链接,而没有更多字段。

    class Product < ActiveRecord::Base
       has_and_belongs_to_many :users
    end
    class User < ActiveRecord::Base
       has_and_belongs_to_many :products
   end

甜菜根的迁移将是:

class CreateUsersProducts < ActiveRecord::Migration
  def change
    create_table "users_products", :id => false do |t|
      t.integer :user_id
      t.integer :product_id
    end
  end
end

实现后,我发现我认为我有正确的关系设置,我必须使用has_many:尽管用户可能有很多产品,但它需要:through,因为collaborator表上有其他属性。症结在于如何让每个用户/产品对只有一个Collaborator记录,然后我如何保证我得到了它。到目前为止,我找到的答案是必须用代码来完成。

为了确保每对只有一条记录,我使用了

class Collaborator < ActiveRecord::Base
  validates :product_id, :presence => true,  :uniqueness => {:scope => [:user_id], :message => "This is a duplicate join"}

然后,为了确保我找到了正确的记录,我有一个范围

 scope :collaboration_instance, lambda {|p_id, u_id| where("collaborations.product_id = ? && collaborations.user_id = ?", p_id, u_id)}

如果有人有更优雅的解决方案,或者只是想改进这个,请发布,我会将您的答案更改为选定的答案。

相关内容

  • 没有找到相关文章

最新更新