何时在 Rails 中使用“ has_many: through”关系?

我试图理解什么是 has_many :through,什么时候使用它(以及如何使用)。但是,我不明白。我正在阅读开始 Rails 3,我尝试了谷歌,但我不能理解。

108414 次浏览

假设你有这些模型:

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

假设你有两个模型: UserGroup

如果希望用户属于组,那么可以这样做:

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 :group_memberships
has_many :groups, :through => :group_memberships  # Edit :needs to be plural same as the has_many relationship
end

现在您可以像对待普通的 has_many一样对待它,但是在需要的时候可以从关联模型中获益。

请注意,您也可以使用 has_one来完成此操作。

编辑: 使向组添加用户变得容易

def add_group(group, role = "member")
self.group_associations.build(:group => group, :role => role)
end

ActiveRecord 联接表

has_many :throughhas_and_belongs_to_many关系通过一个 联合桌起作用,联合桌是一个中间表,表示其他表之间的关系。与 JOIN 查询不同,数据实际上存储在表中。

实际差异

使用 has_and_belongs_to_many,您不需要主键,并且通过 ActiveRecord 关系而不是通过 ActiveRecord 模型访问记录。当您想用多对多关系链接两个模型时,通常使用 HABTM。

当您希望以 Rails 模型的形式与连接表交互时,可以使用 has_many :through关系,该关系具有主键和向连接数据添加自定义列的能力。后者对于与联接行相关、但不属于相关模型的数据尤其重要——例如,存储从联接行中的字段派生的计算值。

参见

活动记录关联指南中,建议如下:

最简单的经验法则是,如果需要将关系模型作为独立实体处理,应该设置一个 has _ many: through 关系。如果您不需要对关系模型进行任何操作,那么设置 has _ and _ properties _ to _ many 关系可能会更简单(尽管您需要记住在数据库中创建连接表)。

如果需要联接模型上的验证、回调或其他属性,应该使用 has _ many: through。