LoginSignup
2
1

More than 5 years have passed since last update.

Rails 4 - Active Record Associations

Last updated at Posted at 2013-03-09

has_one

一对一关系:有 A 和 B,A 拥有且仅拥有一个 B

has_one

belongs_to

一对一关系:有 A 和 B,A 属于且仅属于一个 B

belongs_to

has_one :through

嵌套式一对一关系:A 拥有一个 B,B 拥有一个 C,那么 A 通过 B 拥有一个 C

has_one_through

在 has_one 和 belongs_to 之间进行选择

当你要在两个模型之间建立一对一关系时,你需要为其中一个模型添加 has_one,而为另一个添加 belongs_to,那么如何做出正确的选择呢?

关键之处就在于你把主键安排给哪个模型(拥有主键的模型应该添加 belongs_to),不过在此之前还应该认真考虑一下在现实中的合理性。

has_one 关系意味着某样东西(实例化的对象)是你的。比如,我们会说“一个用户拥有一个账号”,而不是“一个账号拥有一个用户”。那么在这里,主键应该安排在账号模型内,也就是说“账号属于(belongs_to)模型”:

class User < ActiveRecord::Base
  has_one :account
end

class Account < ActiveRecord::Base
  belongs_to :user
end

与之对应的 migration 即是:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end

    create_table :accounts do |t|
      t.integer :user_id
      # t.references :user_id 是一样的
      t.string  :account_name
      t.timestamps
    end
  end
end

has_many

一对多关系:有 A 和 B,A 拥有零个或多个 B

has_many

has_many :through

多对多关系:有 A 和 C,(通过 B)产生多个 A 与 多个 C 之间的两两关系

has_many_through

嵌套式多对多关系

还有一种多对多关系:A 拥有多个 B,B 拥有多个 C,那么 A 通过 B 拥有多个 C

class Article < ActiveRecord::Base
  has_many :sections
  has_many :paragraphs, through: :sections
end

class Section < ActiveRecord::Base
  belongs_to :article
  has_many :paragraphs
end

class Paragraph < ActiveRecord::Base
  belongs_to :section
end

此时,Rails 可以明白这样的关系:

@article.paragraphs

has_and_belongs_to_many

直接多对多关系:每一个 A 拥有或属于多个 B,每一个 B 属于或拥有 多个 A

has_and_belongs_to_many

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

Rails 为我们提供了两种多对多的模型关系,它们的区别在于其中间关系是隐性的还是显性的。

has_and_belongs_to_many 是一种直接化的多对多关系,不需要单独建立中间关系的模型(但是中间关系表需要人工创建),所以是隐性的。而 has_many :through 则需要创建中间关系的模型。

所以,选择哪一种的关键之处就在于你是否需要对中间关系进行进一步的控制,比如说添加额外属性和模型验证,或者添加回调方法等等;换句话说就是要看中间关系是否是业务逻辑里需要出现的实体。

polymorphic

多态关系:有 A, B 和 C,为 A 定义一个接口,于是 B 可以拥有 多个 A,C 也可以拥有多个 A

polymorphic

此时,Rails 将理解以下关系:

@employee.pictures
# or
@product.pictures

并且可以通过 @picture.imageable 访问 Picture 实例的父级关系,当然你需要指明数据库里的类型字段:

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      # 以上两句可以简化为:t.references :imageable, polymorphic: true
      t.timestamps
    end
  end
end

Self Joins

有时候你会发现一个模型需要拥有针对自身的关系,比如说你想要把所有的员工保存在一张表里,并且能够跟踪他们之间的相互关系(经理、下属),此时你可以使用 self-joining:

class Employee < ActiveRecord::Base
  has_many :subordinates, class_name: 'Employee', foreign_key: 'manager_id'
  belongs_to :manager, class_name: 'Employee'
end
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1