我想了解has_many :through
是什么以及何时使用它(以及如何使用)。但是,我没有得到它。我正在阅读Beginning Rails 3,我尝试使用谷歌搜索,但我无法理解。
答案 0 :(得分:180)
假设您有这些模型:
Car
Engine
Piston
汽车has_one :engine
引擎belongs_to :car
引擎has_many :pistons
活塞belongs_to :engine
汽车has_many :pistons, through: :engine
活塞has_one :car, through: :engine
基本上,您将模型关系委托给另一个模型,因此您不必调用car.engine.pistons
,而只需执行car.pistons
答案 1 :(得分:165)
假设您有两个模型:User
和Group
。
如果您想让用户属于群组,那么您可以执行以下操作:
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end
如果您想跟踪关联周围的其他元数据,该怎么办?例如,当用户加入群组时,或者用户角色在群组中的位置?
这是您将关联作为第一类对象的地方:
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :group
# has attributes for date_joined and role
end
这引入了一个新表,并从用户表中删除了group_id
列。
此代码的问题在于您必须更新使用该用户类的其他位置并更改它:
user.groups.first.name
# becomes
user.group_memberships.first.group.name
这种类型的代码很糟糕,它会引起像这样的变化。
has_many :through
为您提供两全其美的优势:
class User < ActiveRecord::Base
has_many :groups, :through => :group_memberships # Edit :needs to be plural same as the has_many relationship
has_many :group_memberships
end
现在您可以将其视为普通has_many
,但在需要时可以获得关联模型的好处。
请注意,您也可以使用has_one
。
修改:轻松将用户添加到群组
def add_group(group, role = "member")
self.group_associations.build(:group => group, :role => role)
end
答案 2 :(得分:15)
has_many :through
和has_and_belongs_to_many
关系通过连接表运行,连接表是表示其他表之间关系的中间表。与JOIN查询不同,数据实际存储在表中。
使用has_and_belongs_to_many
,您不需要主键,而是通过ActiveRecord关系而不是通过ActiveRecord模型访问记录。当您想要链接两个具有多对多关系的模型时,通常使用HABTM。
当您希望与作为Rails模型的联接表进行交互时,使用has_many :through
关系,包括主键以及向联接数据添加自定义列的功能。后者对于与连接行相关的数据尤为重要,但并不真正属于相关模型 - 例如,存储从连接行中的字段派生的计算值。
在A Guide to Active Record Associations中,建议为:
最简单的经验法则是,如果需要将关系模型作为独立实体使用,则应设置has_many:through关系。如果您不需要对关系模型执行任何操作,则设置has_and_belongs_to_many关系可能更简单(尽管您需要记住在数据库中创建连接表)。
如果您需要在连接模型上进行验证,回调或额外属性,则应使用has_many:through。