Image by Amir-abbas Abdolali

Polymorphic Relationships in Rails + Postgres

Don Mallory

--

While writing Controller logic for a personal project of mine, I got to dig into some more advanced features of ActiveRecord, an Object Relational Mapping library to perform CRUD operations.

For the project, a few thousand politicians were being tracked, along with their relevant Twitter accounts and Tweets sent.

To get all of this data organized, ActiveRecord models mapped to a Postgres database made the most sense. But how to organize all the relationships……

The primary function of the app was to serve a user info based on their address. Voting data isn’t specific to an address but to one of 435 congressional districts that get drawn and redrawn every 10 years (see: Gerrymandering). It then made sense to have 50 State models that each has_many of the 435 District models, enabling us to store data specific to states and districts.

But as similar as congresspeople are, Senators belong to States and Rep(resentative)s belong to Districts. This can easily be handled with a has_many through as above, but what happens when both Senators and Reps have something in common? Looking at the picture above, both States and Districts have something called a CookIndex. Drum roll……Enter Polymorphism.

Polymorphic relationships in ActiveRecord enable us to create relations from one model to multiple other models, the latter with different attributes, relationships, and class methods. This was needed as both States and Districts have information available through the Cook Political Report (CookIndex) but I didn’t want to store the information in separate tables, or even worse, in the State and District tables themselves.

When setting a standard belongs_to relationship, we use convention to specify a foreign key in the belongs_to table that correlates to the primary key of our “parent” table. ActiveRecord convention prefers that if we have a District table and a State table, we create a key called state_id and ActiveRecord handles the associations for us. With polymorphism, we alias the polymorphic relationship and create two columns, one with a foreign key and another to reference which model the foreign key “points to.”

To recap, to create a polymorphic relationship:

  1. Set associations from the “owner” side of the relationship with an “alias” passed as an argument, i.e. has_one :cook_index, as: location
  2. Set association on the “child” side, but set it to belong_to the “alias” from Step 1 and specify it is a polymorphic relation, i.e. belongs_to :location, polymorphic: true
  3. Create fields in your child database table that follow convention of your alias_type and alias_id, i.e. location_type (String) and location_id (integer)
  4. To build your associations programmatically, use typical syntax from the owner side (<senator>.twitter_accounts.build(args…) for has_many, etc.) and reference the alias if building from the child side (<cook_index_object>.location = <state_object> or <district_object>). ActiveRecord will fill in the foreign key (location_id) and polymorphic type (location_type) for you.

While a great strength of ActiveRecord is its out-of-the-box conventions, it ultimately has tons of configurable features that enable mapping of more complex objects.

--

--