我第一次使用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)}
如果有人有更优雅的解决方案,或者只是想改进这个,请发布,我会将您的答案更改为选定的答案。