什么时候应该在Rails中使用“has_many:through”关系?

时间:2012-07-22 14:05:44

标签: ruby-on-rails ruby-on-rails-3 activerecord

我想了解has_many :through是什么以及何时使用它(以及如何使用)。但是,我没有得到它。我正在阅读Beginning Rails 3,我尝试使用谷歌搜索,但我无法理解。

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)

假设您有两个模型: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 :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)

ActiveRecord加入表

has_many :throughhas_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。