rails 指南总结(五)——Model之Active Record 关联

1 关联是什么?

关联在两个 Active Record 模型之间建立联系

2 关联的类型

Rails 支持六种关联:

2.1 belongs_to

2.1.1 概述

belongs_to 关联创建两个模型之间一对一的关系,声明所在的模型实例属于另一个模型的实例。例如:图书和作者

class Book < ApplicationRecord
  belongs_to :author
end

对下述声明来说,Book 模型的每个实例都获得了这些方法:

author
author=
build_author
create_author 
create_author!

2.1.2 belongs_to 支持的选项

  • :autosave:把 :autosave 选项设为 true,保存父对象时,会自动保存所有子对象,并把标记为析构的子对象销毁。
  • :class_name:如果另一个模型无法从关联的名称获取,可以使用 :class_name 选项指定模型名。
  • :counter_cache: 提高统计所属对象数量操作的效率,果想知道 @author.books.size 的结果,要在数据库中执行 COUNT(*) 查询。如果不想执行这个查询,可以在声明 belongs_to 关联的模型中加入计数缓存功能:
  belongs_to :author, counter_cache: true
  belongs_to :author, counter_cache: :count_of_books
  • :dependent
    :dependent 选项控制属主销毁后怎么处理关联的对象:

  • :foreign_key
    foreign_key 选项可以设置要使用的外键名:

  • :primary_key
    按照约定,Rails 假定使用表中的 id 列保存主键。使用 :primary_key 选项可以指定使用其他列。

  • :inverse_of
    :inverse_of 选项指定 belongs_to 关联另一端的 has_many 和 has_one 关联名。不能和 :polymorphic 选项一起使用。

  • :polymorphic
    :polymorphic 选项为 true 时,表明这是个多态关联

  • :touch
    如果把 :touch 选项设为 true,保存或销毁对象时,关联对象的 updated_at 或 updated_on 字段会自动设为当前时间。还可指定要更新哪个时间戳字段:

  • :validate
    如果把 :validate 选项设为 true,保存对象时,会同时验证关联的对象。该选项的默认值是 false,保存对象时不验证关联的对象。

  • :optional
    如果把 :optional 选项设为 true,不会验证关联的对象是否存在。该选项的默认值是 false。

2.1.3 belongs_to 的作用域

有时可能需要定制 belongs_to 关联使用的查询,定制的查询可在作用域代码块中指定。

  • where 方法指定关联对象必须满足的条件。
  • includes 方法指定使用关联时要及早加载的间接关联。
  • 如果使用 readonly,通过关联获取的对象是只读的。
  • select 方法用于覆盖检索关联对象使用的 SQL SELECT 子句。默认情况下,Rails 检索所有字段。

2.2 has_one

has_one 关联也建立两个模型之间的一对一关系,表示模型的实例包含或拥有另一个模型的实例。例如,应用中每个供应商只有一个账户

class Supplier < ApplicationRecord
  has_one :account
end

和belongs_to类似

2.3 has_many

2.3.1 概述

  • has_many 关联建立两个模型之间的一对多关系。
  • 在 belongs_to 关联的另一端经常会使用这个关联。has_many 关联表示模型的实例有零个或多个另一模型的实例。例如,作者和图书。
class Author < ApplicationRecord
  has_many :books
end

声明 has_many 关联时,另一个模型使用复数形式。

2.3.2 has_many 关联添加的方法

  • @author.books :返回一个数组,包含所有关联的对象。
  • @author.books << @book1 :<< 方法向关联对象数组中添加一个或多个对象,并把各个所加对象的外键设为调用此方法的模型的主键。
  • @author.books.delete(@book1) :从关联对象数组中删除一个或多个对象,并把删除的对象外键设为 NULL。
  • @author.books.destroy(@book1) :从关联对象数组中删除一个或多个对象。
  • @author.book_ids :singular_ids 方法返回一个数组,包含关联对象数组中各对象的 ID。
  • has_many :articles, -> { distinct }, through: :readings
    以确保集合中没有重复的对象。与 :through 选项一起使用最有用。
    …(更多)

2.4 has_many :through

  • has_many :through 关联经常用于建立两个模型之间的多对多关联。
  • 这种关联表示一个模型的实例可以借由第三个模型,拥有零个和多个另一模型的实例。eg:病人要和医生约定练习时间。
class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end
 
class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end
 
class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

2.5 has_one :through

  • has_one :through 关联建立两个模型之间的一对一关系。
  • 这种关联表示一个模型通过第三个模型拥有另一模型的实例。例如,每个供应商只有一个账户,而且每个账户都有一个账户历史,那么可以这么定义模型:
class Supplier < ApplicationRecord
  has_one :account
  has_one :account_history, through: :account
end
 
class Account < ApplicationRecord
  belongs_to :supplier
  has_one :account_history
end
 
class AccountHistory < ApplicationRecord
  belongs_to :account
end

2.6 has_and_belongs_to_many

has_and_belongs_to_many 关联直接建立两个模型之间的多对多关系,不借由第三个模型。
例如,应用中有装配体和零件两个模型,每个装配体有多个零件,每个零件又可用于多个装配体(Assembly).

class Assembly < ApplicationRecord
  has_and_belongs_to_many :parts
end
 
class Part < ApplicationRecord
  has_and_belongs_to_many :assemblies
end

2.7 多态关联

关联还有一种高级形式——多态关联(polymorphic association)。在多态关联中,在同一个关联中,一个模型可以属于多个模型。
例如,图片模型可以属于雇员模型或者产品模型,

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end
 
class Employee < ApplicationRecord
  has_many :pictures, as: :imageable
end
 
class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

在 belongs_to 中指定使用多态,可以理解成创建了一个接口,可供任何一个模型使用。
在 Employee 模型实例上,可以使用 @employee.pictures 获取图片集合。
类似地,可使用 @product.pictures 获取产品的图片。

2.8 自联结

设计数据模型时,模型有时要和自己建立关系。例如,在一个数据库表中保存所有雇员的信息,但要建立经理和下属之间的关系。这种情况可以使用自联结关联解决

class Employee < ApplicationRecord
  has_many :subordinates, class_name: "Employee",
                          foreign_key: "manager_id"
 
  belongs_to :manager, class_name: "Employee"
end

这样定义模型后,可以使用 @employee.subordinates 和 @employee.manager 检索了。

3 小技巧和注意事项

3.1 控制缓存

关联添加的方法都会使用缓存,记录最近一次查询的结果,以备后用。缓存还会在方法之间共享。

author.books           # 从数据库中检索图书
author.books.size      # 使用缓存的图书副本
author.books.empty?    # 使用缓存的图书副本
author.books.reload.empty?   # 丢掉缓存的图书副本

3.3 更新模式

3.3.1 创建 belongs_to 关联所需的外键

在 books 表中创建相应的外键,为了提升查询性能,最好为外键添加索引;为了保证参照完整性,最好为外键添加约束:


class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string   :book_number
      t.integer  :author_id
    end
    
    add_index :books, :author_id
    add_foreign_key :books, :authors

  end
end

3.3.2 创建 has_and_belongs_to_many 关联所需的联结表

  • 创建 has_and_belongs_to_many 关联后,必须手动创建联结表。除非使用 :join_table 选项指定了联结表的名称,否则 Active Record 会按照类名出现在字典中的顺序为表起名。因此,作者和图书模型使用的联结表默认名为“authors_books”,因为在字典中,“a”在“b”前面。
  • 我们把 id: false 选项传给 create_table 方法,因为这个表不对应模型。只有这样,关联才能正常建立。

3.4 控制关联的作用域

默认情况下,关联只会查找当前模块作用域中的对象。
要想让处在不同命名空间中的模型正常建立关联,声明关联时要指定完整的类名:

module MyApplication
  module Business
    class Supplier < ApplicationRecord
       has_one :account,
        class_name: "MyApplication::Billing::Account"
    end
  end
 
  module Billing
    class Account < ApplicationRecord
       belongs_to :supplier,
        class_name: "MyApplication::Business::Supplier"
    end
  end
end

3.5 双向关联

一般情况下,都要求能在关联的两端进行操作,即在两个模型中都要声明关联。

class Author < ApplicationRecord
  has_many :books
end
 
class Book < ApplicationRecord
  belongs_to :author
end

通过关联的名称,Active Record 能探知这两个模型之间建立的是双向关联。这样一来,Active Record 只会加载一个 Author 对象副本,从而确保应用运行效率更高效,并避免数据不一致。

  • Active Record 能自动识别多数具有标准名称的双向关联。然而,具有下述选项的关联无法识别:
    :conditions
    :through
    :polymorphic
    :class_name
    :foreign_key
  • Active Record 提供了 :inverse_of 选项,可以通过它明确声明双向关联,inverse_of 有些限制:
    不支持 :through 关联;
    不支持 :polymorphic 关联;
    不支持 :as 选项;

3.6 单表继承

$ rails generate model car --parent=Vehicle

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值