Polymorphic Associations in Ruby on Rails

Breanne Marotta
5 min readAug 31, 2022

--

For my final project at Flatiron School, I built a Maintenance Tracker Application. Initially, I planned for the following relationship map:

While building my project, I made a few changes to the way the data relationships work to make everything more functional. For example, with my original map, in order to log a repair, an item had to have at least 1 part and the repair had to be on a part.

But what if the repair was on the actual item? What if I wanted to log that I cleaned the refrigerator? ….there is no part associated with that. I don’t want to attached that repair to the filter part. But, when I change the filter, I want that attached to the filter which is a part of the refrigerator.

I could adjust my data so each repair has item_id and part_id in it’s table…but what if I come up with even more ways to designate what is being repaired?

In most articles and blogs I’ve read, this problem is usually described by way of comments on something in a social media type application. You can comment on a post, a picture, a video, someone else’s comment, someone’s reaction…and so on. Rather than giving a place in the comments table for each of these things upon which one can comment, the solution is to use a polymorphic association.

Rather than limiting repairs to a part of an item, or adding multiple foreign keys to my repairs table, I decided to use a polymorphic association where repairs belong to either a part or an item.

So now Item is directly connected to Repair and Part is also directly connected to Repair

Models:

When you create a polymorphic relationship in Ruby on Rails, the models that has_many are generally named with an -able word. i.e. “commentable”, “postable”, and, in this case, “repairable”. So when you define the relationship, it would normally be has_many :repairs, but now we acknowledge what the repairs are known as. Then, in the belong_to model, we put the “-able” word and :polymorphic => true. This is demonstrated below:

Models with Polymorphic relationship

Forms:

When we add a new repair via a form on the front end, there is specific information that needs to be included in the params we send to the database.

repairable_id & repairable_type need sent through params

For my use case, a part belong_to items, so the user first selects an item through a dropdown menu, then they can select a specific part from a filtered dropdown menu (that only includes parts belonging to that item). If they just select an item and want the repair associated with the item, I need repairable_type to be “Item” and the repairable_id to be that item’s id. But if they go a step further and select a part, I need repairable_type to be “Part” and repairable_id to also be re-assigned to the part’s id.

The functions look like this:

Through these functions, the correct repairable_type and repairable_id are assigned to the repairObj which is sent via a PATCH to the database.

Controller:

This relationship does not change the controller for the repairable models at all. So, at this point, we can leave them alone in the backend, and focus on our polymorphic model controller: Repairs.

We are attempting to take the repairObj generated by completed our form on the client side and doing a create method in our repairs controller.

Step 1: We need to establish the repairable

In our private methods of the repairs controller, we have established our strong params and now we are creating a method to find the repairable:

@klass will be either Item or Part. Then @thing will find the correct Item/Part based on the id provided in params.

Step 2: we need to create a repair with the knowledge of the repairable

Our before_action calls the private method :find_repairable before we create, so we can use @thing to have the correct Item/Part for which we are creating a repair.

Step 3: If no repairable is assigned, we need to roll back and send an error

Views:

On the client side, there are some areas where I needed to determine if the repairable_type was Item or Part. I did this with the following code:

For the variable “thing”, we check to see if repairable_type == “Item”, if so, it searches the list of items (from global state) to find the matching id. If type is not “Item”, we search through the list of parts.

Next, we declare a variable “p”, this checks if repairable_type == “Part”, if it does, we want to check through items to find the item that our thing belongs to, if not, we don’t want to do anything.

Finally, our <h2> is checking if p.name exists? if it does, we know that p is an item and so we want to display the “Item Name” — “Part Name”. If p.name does not exist or if falsey, we just want to display “Item Name”.

  • The Odin Project has a really helpful article for a better understanding of these relationships.

--

--

Breanne Marotta

Hello and welcome to my blog. I am a software engineering student at Flatiron School. I plan to use this blog to communicate what I'm learning on this journey.