LaunchSchool - An Online School for Developers /

Blog

Understanding Polymorphic Associations in Rails

Polymorphic associations can be tricky to understand and many times one can get confused about when to use them, how to use them and how they are different from other associations. In this article we will go over these issues.

Introduction

Active Record Associations are one of the most important features of Rails. Polymorphic association is part of these associations. Ruby on Rails Guide states “with polymorphic associations, a model can belong to more than one other model, on a single association”.

Basics

While writing some rails application you will run into situations when you have model associations that seem to be similar, for example lets assume you have Course and Lab models in your application. Now each of the Course and Lab need Teaching Assistants, so you need associate Course and Labs to their corresponding TAs. If you use has_many/belongs_to kind of association here, you will have two similar models for TAs of course and TAs of Lab. Instead of having two different models you can have a single model i.e TeachingAssistant and you can associate this model with Course and Lab models using polymorphic association.

Lets see how to implement it. First we will need to create TeachingAssistant model.

1
rails g model TeachingAssistant name:string ta_duty_id:integer ta_duty_type:string

Here ta_duty_id is a foreign key and ta_duty_type will tell model TeachingAssistant which model it is associated with.

/db/migrate/********_create_teaching_assistants.rb

1
2
3
4
5
6
7
8
9
10
11
class CreateTeachingAssistants < ActiveRecord::Migration
  def change
    create_table :teaching_assistants do |t|
      t.string :name
      t.integer :ta_duty_id
      t.string :ta_duty_type

      t.timestamps
    end
  end
end

Now our model is generated, to run the migration do rake db:migrate. Now lets set up polymorphic association model TeachingAssistant

1
2
3
class TeachingAssistant < ActiveRecord::Base
  belongs_to :ta_duty, polymorphic: true
end

By making Teaching Assistant belong_to ta_duty instead of any other model we have declared polymorphic association. Note that we do not have any ta_duty model/class in our application, ta_duty is just taking care of polymorphic association. Now lets look at other side.

/app/models/course.rb

1
2
3
class SectionA < ActiveRecord::Base
  has_many :teaching_assistants, as: :ta_duty
end

/app/models/lab.rb

1
2
3
class Lab < ActiveRecord::Base
  has_many :teaching_assistants, as: :ta_duty
end

Code above says that Course and Lab has many TAs via polymorphic association ta_duty. Lets take a look a pictorial representation of polymorphic association we are setting up. We can see how model TeachingAssistant is associated with two model Course and Lab in single association via ta_duty which is helping in achieving the association.

Now lets test this in rails console. Type rails c and follow the commands.

1
2
3
4
5
6
7
8
Loading development environment (Rails 4.1.1)
2.0.0-p247 :001 > ta = TeachingAssistant.create(name: 'ta_name')
2.0.0-p247 :002 > c = Course.create(name: 'course_name')
2.0.0-p247 :003 > ta.update_attribute(:ta_duty, c)
 => true
2.0.0-p247 :004 > Course.last.teaching_assistants.last.name
 => "ta_name"
2.0.0-p247 :005 >

We added TeachingAssistant details, created Course and created association between Course and TeachingAssistant via polymorphic association ta_duty. In line six we tested if the association works.

STI vs Polymorphic Association

Single Table Inheritance(STI) is often compared with polymorphic associations. It can a good alternative to polymorphic associations(depending upon the situation). In STI you inherit similar models from base model which inherits from ActiveRecord::Base, so in our case

1
2
3
class TeachingAssistant < ActiveRecord::Base
class CourseTa < TeachingAssistant
class LabTa < TeachingAssistant

From following figure you will get clear picture how single table inheritance is different from polymorphic association and see how STI looks like.

As we can see STI provides you with more flexibility than polymorphic association but we have to create separate classes to implement the associations. STI adds lot more code and can be difficult to implement compared to polymorphic association. Although you should take final decision depending on use case. In our case polymorphic association is clearly a better choice than STI.

Combining has_many :through and polymorphic association

There can be special cases where you need has_many :through with polymorphic association. Although not difficult to understand/implement, one might get confused the first time. So lets say Professor wants to know what courses/labs were assigned to his phd students/TAs. This can be made possible by doing the following.

/app/models/professor.rb

1
2
3
4
5
class Professor < ActiveRecord::Base
  has_many :teaching_assistants
  has_many :course_tas, through: :teaching_assistants, source: :ta_duty, source_type: 'Course'
  has_many :lab_tas, through: :teaching_assistants, source: :ta_duty, source_type: 'Lab'
end

/app/models/teaching_assistant.rb

1
2
3
4
class TeachingAssistant < ActiveRecord::Base
  belongs_to :professors
  belongs_to :ta_duty, polymorphic: true
end

That’s it, we have combined has_many :through and polymorphic association. To test you can make queries like Professor.first.course_tas. Note that we have added association between Professor and TAs, so make sure you make corresponding changes.

Limitations

When adding Bi-directional Associations in rails application, one uses inverse_of: option provided by Active Record. inverse_of: does not work with polymorphic associations.

Conclusion

We covered basics & implementation of polymorphic associations in rails, compared it with other associations and saw how it can be extended with other associations. Once you understand correct and appropriate use of polymorphic association you can make your rails application more efficient.

References/Resources