Ruby on Rails: Polymorphic Association

June 19th, 2008 | by hantu |

While working on our industrial project today, we stumbled across this situation where we need a base class (Document) that are able to refer to different type of documents (ie. Report, Screen, etc).

We found two ways of dealing with this situation, one of which is by using Single Table Inheritance (STI); and the other one we are fond of, is called Polymorphic Association.

According to [1], Single Table Inheritance is:

… an inheritance hierarchy of classes as a single table that has columns for all the fields of the various classes

We decided to go with Polymorphic Association, because we wanted different classes for different subtypes of documents. We wanted to avoid having a really large single table to store everything, and using STI might leave us with many empty fields in a row.

A little explanation of Polymorphic Association, based on my understanding. Polymorphic Association uses two fields in the base class as the foreign keys, to refer to different subclasses. It stores the ID of the subclass, and the type (model).

This is the relationship we hope to achieve:

Inheritance

I have created models for the above classes:

script/generate model Document
script/generate model Report
script/generate model Screen
script/generate model Process

We start by declaring the interface, and associate them:

We declare the interface in models/document.rb:

class Document < ActiveRecord::Base
  belongs_to :content, :polymorphic => true
end

Then, we associate the subclasses with the interface.

In models/process.rb:

class Process < ActiveRecord::Base
  has_one :document, :as => :content
end

In models/report.rb:

class Report < ActiveRecord::Base
  has_one :document, :as => :content
end

In models/screen.rb:

class Screen < ActiveRecord::Base
  has_one :document, :as => :content
end

Now, in Document’s migration file, create the table:

create_table :documents do |t|
  t.string :title
  t.string :author
  t.references :content, :polymorphic => true
  t.timestamps
end

In the rest of the migration files (create_report, create_screen, create_process), fill in the appropriate columns for your table, and finally run the migration.

rake db:migrate

What you will see in your database table (documents) is that Rails created two fields, namely content_id and content_type in your table. Those are the fields used to reference the subclasses.

So far, this method is able to accomplish what we had in mind.

If anyone out there have a better solution to this problem, drop me a comment. Cheers.

ref:

  1. P of EAA: Single Table Inheritance

You must be logged in to post a comment.