Polymorphic Tables in Ruby on Rails

A quick overview of ActiveRecord polymorphic associations.

Photo by Cécile Brasseur on Unsplash

When developing our applications, we often encounter situations where two or more different models have associations that seem to be identical. For instance, we may have a Product and a Service model that have many photos, or a Project, a Contractor, and a Task model that have many comments. One way to approach this problem would be to use one child database table for each parent model:

class Product < ApplicationRecord
has_many :photos, class_name: 'ProductPhotos'
end
class Service < ApplicationRecord
has_many :photos, class_name: 'ServicePhotos'
end
class Project < ApplicationRecord
has_many :comments, class_name: 'ProjectComments'
end
class Contractor < ApplicationRecord
has_many :comments, class_name: 'ContractorComments'
end
class Task < ApplicationRecord
has_many :comments, class_name: 'TaskComments'
end

Alternatively, we can use ActiveRecord polymorphic associations to store these similar associations in a single database table. According to the Ruby on Rails Guide, polymorphic associations can be defined as follows:

With polymorphic associations, a model can belong to more than one other model, on a single association

We could generate the following models:

rails g model Photo imageable:references{polymorphic}
rails g model Comment commentable:references{polymorphic}

Which creates the following migrations:

class CreatePhotos < ActiveRecord::Migration[5.2]
def change
create_table :photos do |t|
t.references :imageable, polymorphic: true
t.timestamps
end
end
end
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.references :commentable, polymorphic: true
t.timestamps
end
end
end

And the corresponding ActiveRecord associations would be defined as such:

class Photo < ApplicationRecord
belong_to :imageable
end
class Product < ApplicationRecord
has_many :photos, as: :imageable
end
class Service < ApplicationRecord
has_many :photos, as: :imageable
end
class Comment < ApplicationRecord
belongs_to :commentable
end
class Project < ApplicationRecord
has_many :comments, as: :commentable
end
class Contractor < ApplicationRecord
has_many :comments, as: :commentable
end
class Task < ApplicationRecord
has_many :comments, as: :commentable
end

Underneath the hood, the polymorphic migration created two columns in each polymorphic table: imageable_id and imageable_type in Photos and commentable_id and commentable_type in Comments. Because a polymorphic table can belong to more than one other model, ActiveRecord uses the polymorphic_type column to determine the class name of the association from to find the correct instance with the polymorphic_id.

In our examples above, imageable_type is a string which can take the value of Product or Service, and imageable_id is a foreign key from one of these two tables, whereas commentable_type is a string which can take the value of Project, Contractor or Task, and commentable_id is a foreign key from one of those three tables.

--

--

Web development in Ruby on Rails, React, Vue.js and Elixir

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Rui Freitas

Rui Freitas

Lead Teacher @ Le Wagon | Web Developer @ Light the Fuse and Run: http://lightthefuse.run/ | Photographer @ Rod Loboz: https://blog.rodloboz.com/