在 has_many :through 和 has_and_belongs_to_many 关联之间进行选择

Choosing between has_many :through and has_and_belongs_to_many association

我是编程新手,很难确定我需要的模型关联。我已经有两个表:具有 1:many 关系的组织和用户,因为一个组织有多个 users/members。尽管并非所有用户都是组织的成员,但这不是问题。

# In the organization model:
has_many :users

# In the user model:
belongs_to :organization, inverse_of: :users

# In the users migration file:
t.references :organization, index: true, foreign_key: true

我需要用主持人功能来扩展它:一个用户可以是多个组织的主持人,一个组织可以有多个用户作为他们的主持人。因此,用户和组织模型之间的关系将有两个不同的原因(用户作为组织的成员,用户作为组织的版主;在成为版主之前不需要成员)。

我阅读 here 关于创建关联的两种不同可能方式,即 has_many :throughhas_and_belongs_to_many

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.

我不确定 "if you need to work with the relationship model as an independent entity" 是什么意思。在我的用例中,主持人将拥有额外的权利。我相信,该关联将以两种方式使用:1) 显示组织的版主,以及显示用户是其版主的组织;和 2) 查看用户是否具有主持人权限以授予他这些权限。所以我认为我不需要验证(组织本身可以 select 用户作为版主)或额外的属性。我不确定回调(不知道它们到底是什么)。

简而言之,在我的用例中,两种关联形式中哪一种是最好的关联?

当人们问我时我的简明解释:

  • habtm – 只有一个连接table,例如,两个id
  • hm:t – 一个连接 table 包含关于连接关系的元数据,例如,两个 ID 和其他数据,如时间、计数等。

"if you need to work with the relationship model as an independent entity" 意味着您将能够像任何其他 ActiveRecord 模型一样对关系模型进行操作,即您可以创建、保存、销毁等记录或为关系分配各种类型的属性。

例如,假设用户可能与组织有不同类型的关系(例如版主、员工、股东等)。使用 :through 关联,您将拥有:

class Relationship < ActiveRecord::Base
  belongs_to :user
  belongs_to :organization
end

class User < ActiveRecord::Base
  has_many :organizations, through: relationships
end

class Organization < ActiveRecord::Base
  has_many :users, through: relationships
end

# schema.rb
ActiveRecord::Schema.define(:version => 20150511223747) do
   # . . . . 
   create_table "relationships", :force => true do |t|
      t.string   "relationship_type"
      t.integer  "user_id"
      t.integer  "organization_id"
      t.datetime "created_at"
      t.datetime "updated_at"
   end
end

# things you can do: 
org = Organization.first
moderators_or_employees = org.users.where("relationship_type = 'moderator' OR relationship_type = 'employee'")

new_moderators = org.users.where("relationship_type = 'moderator' AND DATE(relationships.created_at) > '2015-01-01'")

这些是您无法使用 has_and_belongs_to_many 关联执行的操作,因为您无法查询关系类型或 created_at。由于这些限制,我经常发现 :through 关系虽然有点复杂,但可以提供更多的灵活性。