Rails开发细节《七》ActiveRecord Associations关联

1.为什么需要关联

很多时候,比如说电子商务中的用户和订单,一个用户会有很多的订单,一个订单只属于一个用户,这就是一种关联。

在创建订单的时候需要用户主键作为外键,删除用户的的同时需要删除用户的订单。

在rails中可以向下面这样订单关联。

 
  
  1. class Customer < ActiveRecord::Base 
  2.   has_many :orders:dependent => :destroy 
  3. end 
  4.   
  5. class Order < ActiveRecord::Base 
  6.   belongs_to :customer 
  7. end 

就可以像下面这样创建订单,删除用户。

 
  
  1. @order = @customer.orders.create(:order_date => Time.now) 
  2.  
  3. @customer.destroy 

2.关联的类型

有下面6中关联。

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

 

2.1.belongs_to

belongs_to是一种一对一的关联。表达一种属于的关系。

就像一个订单只能属于个用户。在订单表会有一个字段存储用户主键,这个字段是订单表的外键。

 

 
  
  1. class Order < ActiveRecord::Base 
  2.   belongs_to :customer 
  3. end 

2.2.has_one

has_one也是一种一对一的关联。表达一种有一个的关系。

就像一个供应商只能有一个账户。账户表有一个供应商主键,是账户表的外键。

 

 
  
  1. class Supplier < ActiveRecord::Base 
  2.   has_one :account 
  3. end 

2.3.has_many

has_many是一种一对多的关联。表达有多个的关系。

就像一个用户有多个订单。

 

 
  
  1. class Customer < ActiveRecord::Base 
  2.   has_many :orders 
  3. end 

has_many关联的名称需要使用复数形式。

2.4.has_many :through

has_many是一种多对多的关联。存在一个中间的model。是通过中间model建立关联。

有一个场景就是病人看病,但是需要和医生进行预约。一个医生会有多个预约记录,一个病人也会有多个预约记录。在预约表中会有医生主键和病人主键。

 
  
  1. class Physician < ActiveRecord::Base 
  2.   has_many :appointments 
  3.   has_many :patients:through => :appointments 
  4. end 
  5.   
  6. class Appointment < ActiveRecord::Base 
  7.   belongs_to :physician 
  8.   belongs_to :patient 
  9. end 
  10.   
  11. class Patient < ActiveRecord::Base 
  12.   has_many :appointments 
  13.   has_many :physicians:through => :appointments 
  14. end 

 

 其实就是把医生和病人的预约关系单独表存放,这张表也会有主键。

 

 
  
  1. class Document < ActiveRecord::Base 
  2.   has_many :sections 
  3.   has_many :paragraphs:through => :sections 
  4. end 
  5.   
  6. class Section < ActiveRecord::Base 
  7.   belongs_to :document 
  8.   has_many :paragraphs 
  9. end 
  10.   
  11. class Paragraph < ActiveRecord::Base 
  12.   belongs_to :section 
  13. end 

2.5.has_one :through

 

 
  
  1. class Supplier < ActiveRecord::Base 
  2.   has_one :account 
  3.   has_one :account_history:through => :account 
  4. end 
  5.   
  6. class Account < ActiveRecord::Base 
  7.   belongs_to :supplier 
  8.   has_one :account_history 
  9. end 
  10.   
  11. class AccountHistory < ActiveRecord::Base 
  12.   belongs_to :account 
  13. end 

2.6.has_and_belongs_to_many

has_and_belongs_to_many也是一种多对多的关联。不存在一个中间的model。关系在单独的表中存放,但是这张表没有单独的id,只有双方的id。

 

 
  
  1. class Assembly < ActiveRecord::Base 
  2.   has_and_belongs_to_many :parts 
  3. end 
  4.   
  5. class Part < ActiveRecord::Base 
  6.   has_and_belongs_to_many :assemblies 
  7. end 

2.7.选择belongs_to还是has_one

belongs_to一般放在有外键的model中,表达一种属于的关系。has_one表达一种拥有的关系。

 

 
  
  1. class Supplier < ActiveRecord::Base 
  2.   has_one :account 
  3. end 
  4.   
  5. class Account < ActiveRecord::Base 
  6.   belongs_to :supplier 
  7. end 

供应商有一个账号,一个账号属于供应商。

2.8.选择has_many :through还是has_and_belongs_to_many

如果你需要关系model作为独立的实体,就选择has_many :through;不需要独立的实体就选择has_and_belongs_to_many。

 

 
  
  1. class Assembly < ActiveRecord::Base 
  2.   has_and_belongs_to_many :parts 
  3. end 
  4.   
  5. class Part < ActiveRecord::Base 
  6.   has_and_belongs_to_many :assemblies 
  7. end 

 

 
  
  1. class Assembly < ActiveRecord::Base 
  2.   has_many :manifests 
  3.   has_many :parts:through => :manifests 
  4. end 
  5.   
  6. class Manifest < ActiveRecord::Base 
  7.   belongs_to :assembly 
  8.   belongs_to :part 
  9. end 
  10.   
  11. class Part < ActiveRecord::Base 
  12.   has_many :manifests 
  13.   has_many :assemblies:through => :manifests 
  14. end 

如果在连接实体上需要验证,回调,或者额外的属性,那就需要使用has_many :through。

2.9.polymorphic

使用polymorphic关联,一个model可以多个model。

就像图片model,既属于员工model,也属于产品model。就是说员工和产品都有图片,他们共享同一个图片model。

 

 
  
  1. class Picture < ActiveRecord::Base 
  2.   belongs_to :p_w_picpathable:polymorphic => true 
  3. end 
  4.   
  5. class Employee < ActiveRecord::Base 
  6.   has_many :pictures:as => :p_w_picpathable 
  7. end 
  8.   
  9. class Product < ActiveRecord::Base 
  10.   has_many :pictures:as => :p_w_picpathable 
  11. end 

 

 
  
  1. class CreatePictures < ActiveRecord::Migration 
  2.   def change 
  3.     create_table :pictures do |t| 
  4.       t.string  :name 
  5.       t.integer :p_w_picpathable_id 
  6.       t.string  :p_w_picpathable_type 
  7.       t.timestamps 
  8.     end 
  9.   end 
  10. end 

 

 
  
  1. class CreatePictures < ActiveRecord::Migration 
  2.   def change 
  3.     create_table :pictures do |t| 
  4.       t.string :name 
  5.       t.references :p_w_picpathable:polymorphic => true 
  6.       t.timestamps 
  7.     end 
  8.   end 
  9. end 

2.10.自连接

有时候,实体会连接自己。

就好比员工表,有些员工同时又是经理,会领导一部分的员工,这样就会造成自连接。

 

 
  
  1. class Employee < ActiveRecord::Base 
  2.   has_many :subordinates:class_name => "Employee"
  3.     :foreign_key => "manager_id" 
  4.   belongs_to :manager:class_name => "Employee" 
  5. end 

 

参考文献

1.Active Record Associations