polymophic主要是为了解决一种model belongs to多种model的问题(或者说,多种model has一种类似结构的model,但又不想针对前者的每一种去设计一种对应的后者,的问题)

例如Article和Photo都可以有Comment,但如果分两种ArticleComment、PhotoComment十分重复了,况且要是在以后再加多一种关系,Video有Comment,那难道又对应地增加VideoComment类?

如果只用一个Comment类,(在不使用polymophic时)类定义就会如下,而表也需要带有article_id、photo_id类。同样地,如要增加“Video有Comment”这一关系,则类中需同时增加belong_to :video,表中也要增加video_id列,牵动过多。此外,要保证comments表的多个xxx_id外键只能只能在同一时刻最多填一个,也是件麻烦的事

class Comment < ActiveRecord::Base
  belongs_to :articles
  belongs_to :photos
end


于是,我们可以使用一种polymophic的设计:在从表中,除了外键,还定义一列用于记录其主表名,如此一来,便解决了被动加栏位的问题。这当然并不是rails发明的,只是值得的一提的是,rails为polymophic所提供的一套十分可读易懂的dsl

model定义如下。Comment属于commentable的对象

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end


Article和Photo都是commentable的,它们拥有comment

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable
end

class Photo < ActiveRecord::Base
  has_many :comments, as: :commentable
end


而comments表的定义则需要有xxx_id和xxx_type,其中xxx必须同belong_to、as中所指定的

create_table :comments do |t|
  t.text :body
  t.integer :commentable_id
  t.string :commentable_type
end

add_index :comments, [:commentable_type, :commentable_id]


容易想象,当要从主model获取从model时,会取as拼接出xxx_id和xxx_type、并以主model的id和类名填充这两个查询条件。而从从model获取主model时,则取当前记录的xxx_id和xxx_type来构造sql的where和from