loading...

Rails' Polymorphic Associations

adjoa profile image Adjoa ・3 min read

In this post I'll give you an overview of how polymorphic associations work in Rails and how to add them to your projects.

How I Got Here

I'm on my final lap at Flatiron School now and completing my final project. If the last 4 cycles of project-building have taught me anything, it’s the importance of considering the data. ‘What sorts of models will be appropriate and how should they be associated in the database?’

To answer the first question, I wanted to build an application that would allow users to browse through upcoming events. I also wanted organizers to be able to log in and add to the listings. So, 2 models then: Organizer and Event. But both of these models need an Address, right? 3 models. But, how to associate them? This question led me right into unfamiliar territory: polymorphic associations.

What's Polymorphism?

Working with Rails’ Active Record library up to that point I had encountered and come to understand most of the available Active Record associations: belongs_to, has_many, has_many :through, and so on. But in this instance, I needed a way to associate two different models with one model while keeping things DRY. Specifically, I needed a way to associate an Organizer and an Event with an Address that didn’t involve creating separate Address-like models for each.

A polymorphic association to the Address turned out to be the perfect tool for accomplishing this because as the documentation explains, with this type of association “a model can belong to more than one other model, on a single association”.

Ok, but how do I implement this?

We can think of implementing a polymorphic association as creating an interface between the reusable model and the models that need to connect to it. The conventional way to name such interfaces is by adding an '-able' to the end of the model name so, addressable, commentable, imageable etc.

class Address < ApplicationRecord
# name the interface to the Address model addressable
# establish that it is polymorphic
belongs_to :addressable, polymorphic: true
end

class Organizer < ApplicationRecord
# establish a relationship to the Address model 
# via the addressable interface
has_one :address, as: :addresssable
end

class Event < ApplicationRecord
# establish a relationship to the Address model
# via the addressable interface 
has_one :address, as: :addressable
end

I won't lie. When I saw that belongs_to line, my initial reaction was, 'Cool. So there's an Addressable class somewhere?'. Nope! That line names the interface on our polymorphic model. Once the relationship between the polymorphized model and it’s parent is created, it is possible to refer to the parent like this: @address.addressable. We'll also be able to use statements like @organizer.address and @event.address.

To make this work, you need to declare both a foreign key column and a type column in the model that will point to the parent model, Organizer or Event.

class CreateAddresses < ActiveRecord::Migration[5.2]
  def change
    create_table :address do |t|
    t.string :line1
    t.string :line2
    t.string :city
    t.string :state
    t.string :zip
    t.integer :addressable_id
    t.integer :addressable_type
    t.timestamps
    end

    add_index :addresses, [:addressable_type, :addressable_id]
  end
end

This migration can be simplified by using the t.references form:

class CreateAddresses < ActiveRecord::Migration[5.2]
  def change
    create_table :address do |t|
    t.string :line1
    t.string :line2
    t.string :city
    t.string :state
    t.string :zip
    t.references :addressable, polymorphic: true, index: true
    t.timestamps
    end
  end
end

The migration can be generated like this:

rails g migration create_addresses line1:string line2:string city:string state:string zip:string addressable_id:integer addressable_type:string

Or, like this:

rails g migration create_addresses line1:string line2:string city:string state:string zip:string addressable:references{polymorphic}

Conclusion

Polymorphic associations allow one model to belong to many models while keeping code nice and DRY. This kind of association enables such relationships by creating a special interface on the polymorphic or reusable model that allows other models to interact with it. Not convinced this is the pattern for you? Check out this post by Jared Carroll where he guides readers through a more in-depth exploration of this approach.

Posted on by:

adjoa profile

Adjoa

@adjoa

Full-stack web developer. Amateur musician. Incurable bookworm.

Discussion

markdown guide
 

Thank for the nice blog.

I came across this rails generate model product supplier:references{polymorphic}. And left me with alot of questions:-

  1. This creates a model product and foreign key supplier as polymorphic, is it correct?
  2. No use of -able suffix?