138

我试图了解has_many :through它是什么以及何时使用它(以及如何使用它)。但是,我不明白。我正在阅读Beginning Rails 3 并尝试使用谷歌搜索,但我无法理解。

4

3 回答 3

240

假设你有这些模型:

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

于 2012-07-22T14:44:41.113 回答
190

假设您有两个模型: 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
于 2012-07-22T14:47:22.737 回答
21

ActiveRecord Join Tables

has_many :through and has_and_belongs_to_many relationships function through a join table, which is an intermediate table that represents the relationship between other tables. Unlike a JOIN query, data is actually stored in a table.

Practical Differences

With has_and_belongs_to_many, you don't need a primary key, and you access the records through ActiveRecord relations rather than through an ActiveRecord model. You usually use HABTM when you want to link two models with a many-to-many relationship.

You use a has_many :through relationship when you want to interact with the join table as a Rails model, complete with primary keys and the ability to add custom columns to the joined data. The latter is particularly important for data that is relevant to the joined rows, but doesn't really belong to the related models--for example, storing a calculated value derived from the fields in the joined row.

See Also

In A Guide to Active Record Associations, the recommendation reads:

The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).

You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.

于 2012-07-22T15:20:01.383 回答