The Problem
Sharey is an application whose value lies in allowing users to share web content easily between friends. When Matt decides to invite a new friend who has not yet registered, I would like Matt to be able to share things with this new, unregistered user so that when this user does sign up he/she may see the content that people have previously shared with him/her.
Since I’m authenticating users using Google’s OAuth2 protocol, maintaining fidelity to Google’s response hash and its user table was a priority. I created a new model for an Unregistered User.
The Models - Relationships in Question
- User -
has_many :items
- UnregisteredUser -
has_many :items
- Item - Will be polymorphic, and belong_to either a User or UnregisteredUser
How polymorphic associations work
There are two steps involved in setting up a polyorphic relationship.
- Adjusting your database table to include a new field. Since I wanted the user_id field in the Item model to point to either the User model or UnregisteredUser model, I needed to add a user_type field to Item.
- Defining the polymorphic relationships in the ActiveRecord models: Item, User, UnregisteredUser.
Note: There’s a third step often required, where you’ll need to adjust any code that relies on the relationships between these models. I’ll discuss more below.
Setting up the database
Let’s get the database setup first:
Relationships
Next, time to define our relationships. In the Item model, setting up a polymorphic relationship is as easy as adding the polymorphic option to your belongs_to method:
In the User and Unregistered models:
Easy!
All my tests are failing!
Luckily we have nice test coverage in this application. Here’re some of the things that needed changing and a few suprises along the way:
Fixtures
You need to tell your fixtures about the new polymorphic relationship by adding the associated model in brackets.
Uniqueness
I needed to update my uniqueness constraints. Since Items can now belong to either a User or an UnregisteredUser, it is no longer sufficient to check for uniqueness on just the user_id field:
Eager Loading
To avoiding hitting the database (ie the N+1 problem), I was eager loading Users referenced by a collection of Items. Since our user_id field no longer points to a single database table, this is no longer possible in this way. I need to find a different way to use eager loading, other than the includes(:user)
method.
Conclusion
Overall, Rails and ActiveRecord combine to make setting up, testing and using polymorphic relationships very easy.
Hope this helped!