rails中的模型关联(进阶篇)

作者:周星 发布:2017-10-07

       总结了一些 rails 模型关联,有些可能不是很常见,但是会很有用,在这里和大家分享一下。

1. has_many :through

        has_many 的用法大家可能都很熟悉,但是后面跟一个 `:through` 呢? has_many :through 通常表示两个模型之间的多对多的关系是通过(through)另外一个 model 关联起来的,举个例子:一个男孩儿可以通过约会交往多个女孩儿,同时一个女孩儿可以通过约会交往多个男孩儿,相关的`model`代码如下:

class Boy < ActiveRecord::Base
  has_many :appointments
  has_many :girls, :through => :appointments
end

class Appointment < ActiveRecord::Base
  belongs_to :boy
  belongs_to :girl
end

class Girl < ActiveRecord::Base
  has_many :appointments
  has_many :boys, :through => :appointments
end

       注:此时 boy 和 girl 是通过 appointments 关联,所以 boy 表不需要 girl_id,girl 表也不需要 boy_id,只要在 appointment 表声明一个 boy_id和一个 girl_id 即可

rails g scaffold boy name:string age:integer
rails g scaffold girl name:string age:integer
rails g model appointment boy_id:integer girl_id:integer content:string

has_many :through还可以简化嵌套的关联关系,举个例子:某公司有多名开发(developer),每个开发有多次的加班(extra_work),可能你想知道某公司有多少的加班,你应该这样做:

class Company < ActiveRecord::Base
  has_many :developers
  has_many :extra_works, :through => :developers
end

class Developer < ActiveRecord::Base
  belongs_to :company
  has_many :extra_works
end

class ExtraWork < ActiveRecord::Base
  belongs_to :developer
  belongs_to :company
end

这样你就可以使用@company.extra_works.count 了。

2. has_one :through

通过上面介绍的 has_many :through,你应该很容易了解 has_one :through 的适用场景和使用方法了。需要注意的是数据库模型 id 的声明。

3. has_and_belongs_to

has_and_belongs_to 直接建立两个 model 的多对多关系,不需要中间 model,举个例子:一名开发(developer)可以有多个项目(project),一个项目可以有多个开发

class Developer < ActiveRecord::Base
  has_and_belongs_to_many :projects
end

class Project < ActiveRecord::Base
  has_and_belongs_to_many :developers
end

注:这种声明关系需要在数据库中创建中间表

4. 谁应该 has_one? 谁应该 belongs_to?

当你想建立一个一对一的关系时,你需要在他们的 model 上分别声明 has_one 和 belongs_to,那么谁应该 has_one,谁应该 belongs_to 呢?这要看外键的位置,而且你需要考虑到这两个 model 的实际意义,比如说:“每个公民都有且只有一个身份证”要比“每个身份证都有一个公民”更符合实际。

class Citizen < ActiveRecord::Base
  has_one :card
end

class Card < ActiveRecord::Base
  belongs_to :citizen
end

正确的migration应该是这样

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

    create_table :cards do |t|
      t.integer :citizen_id
      t.string  :card_number
      t.timestamps
    end
  end
end

在当前版本的 rails 中,t.integer 那一行应该用 t.references :citizen 代替。

5. has_many :through 还是 has_and_belongs_to_many?

       Rails 提供了两种建立多对多关系的实现方法,简单一点的时是 has_and_belongs_to_many,他直接建立关系,另外一种是声明 has_and_belongs_to_many,它需要通过一个中间 model 建立关系。记住一个最简单的规则:如果你把这个关系模型(比如上面的 appointment )当做一个独立的实体进行操作,你就需要使用 has_many :through,否则就使用 has_and_belongs_to_many 好了(你还是需要在数据库中创建一个中间表)

6. 多态关系

       Rails model 还有一种比较“绕”的关系,我把它翻译为“多态关系”,通过多态关系,一个 model 可以通过一个关系 belongs_to 多个 model,举个例子:你有一个 picture model,它 belongs_to employee 或者 product,你应该做如下声明:

class Picture < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
end

class Employee < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

class Product < ActiveRecord::Base
  has_many :pictures, :as => :imageable
end

        你可以认为一个多态的 belongs_to 声明创建了一个其它 model 使用的接口,你可以使用 @employee.pictures 和 @product.pictures。
       如果你有一个 Picture 实例,你可以通过 @picture.imageable 来获取它的父元素,为了达到这一目的,你需要指定外键和一个 type 字段来声明这个多态接口。

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end
  end
end

有一个更简单的方法,那就是使用 t.references 和 polymorphic

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string :name
      t.references :imageable, :polymorphic => true
      t.timestamps
    end
  end
end

7. 自链接

       在设计一个数据模型的时候,你有时可能需要找到一个 model 对它自己的关系,举个例子:你想要在一个数据库模型中存储所有的员工,但是你还想在这里声明:高管,低级人员等,你可以这么做:

class Employee < ActiveRecord::Base
  has_many :subordinates, :class_name => "Employee",
    :foreign_key => "manager_id"
  belongs_to :manager, :class_name => "Employee"
end

这样你就可以使用 @employee.subordinates和 @employee.manager了。

关于 rails 的模型关联先说到这里,我们下期再见

支付宝扫码赞助博主


评论(0)