How do Rails Polymorphic Tables work Anyway?
Something I had heard about when I initially learned how to code with Ruby on Rails was polymorphic tables. What I didn’t know is why they were useful or how they work and that’s what I’m going to be covering here.
Basically a polymorphic table is a child table that can include a sort of “middle part” or glue where that glue will represent the parent table while child tables interact with parent tables. That glue is what is called a polymorphic association.
An Example
The most simple example where this could apply is a social media app. A user (parent table) can make something they own like a photo or a comment. For our cases, I’m going to call that relationship (aka the glue) the following: reference_to_user (polymorphic association). With the polymorphic set up, whenever a reference_to_user is made for a photo it will represent the user.
User <-> reference_to_user <-> photo
Which used for another child model (comment) could be written as:
User <-> reference_to_user <-> comment
Put into practice when creating a photo using this relationship, it would look like this:
def add_photonew_photo = Photo.create!({ reference_to_user: @user, asset: params[:picture_url })
render json: {
new_photo_id: new_photo.id
}, status: 200
end
The set up for the models is what makes this work before we even get the ability to write methods like the above code block. So for the polymorphic table a line that looks like this will be added in the child model (photo or comment):
belongs_to :reference_to_user, polymorphic: true
And for the parent model, in this case our user, this line would be written in the user model so that it could reference a photo correctly.
has_many :photos, as: :reference_to_user, class_name: “Photo”
This means when the reference_to_user attribute is written in the add_photo method, it refers to the polymorphic association between a user and a photo.
Also within the photo model itself anytime you have a polymorphic table set up, there will be an _id and _type attribute set up on the child model that will refer to the parent class (user).
#Table name: photos
#
#id :integer
#reference_to_user_id :integer
#reference_to_user_type :string
The _type attribute will refer to a string of the class to make the parent object accessible. So when looking at a sample record below it would refer to “User”. And the id would refer to the user id that is associated to the photo.
=> #Photo: 0x00fk8s3
id: 1,
reference_to_user_id: 12,
reference_to_user_type: “User”
asset: picture.png
>
Whatever the polymorphic association (reference_to_user) represents is going to reference the parent resource because a user has many photos.
So the main reason why we’re writing this part of the first code block:
reference_to_user: @user
is to give photo (child) a reference to the parent class so it would know which parent object (which user) it’s creating a photo for.
Hypothetically if we didn’t add this part of the code in there based on our polymorphic set up, we would get a reference error that basically translates to “can’t create this without association reference to the parent class”.
Why are polymorphic associations useful?
For our case, a polymorphic association makes it easy to target a specific user to create a photo for them depending on where the add_photo action is called. It makes it handy since millions of users could exist.
But more importantly, polymorphic relationships allow you to represent multiple parent tables to connect them to child tables by using a single relationship.
For example what if we wanted to have photos belong to a group of people on the social media app as the parent class? Now the “user” for our previous case would technically be represented by a group of people. This means we could rewrite our code block from above this way to create a photo for a specific group instance of the Group model:
def add_photonew_photo = Photo.create!({ reference_to_user: @group, asset: params[:picture_url })
render json: {
new_photo_id: new_photo.id
}, status: 200
end
I’ve made a more visual representation on how these tables are set up below. If you’d like to make something similar, I suggest using Miro which was used in this diagram I made.
Hopefully this was able to shed some light on how these work. You could also think of polymorphic associations as a spokesperson for multiple famous people (parent tables) all for different desperate crazy crowds (child tables).
Speaking of crazy crowds (that aren’t afraid of COVID exposure) it kind of reminds me of a certain event from this past summer…