Rails: Join on polymorphic association

Youssef Chaker
Bear & Giraffe
Published in
3 min readOct 21, 2022

I recently had to come up with a query that involved joining on a polymorphic association, so naturally I Googled how best to implement such a solution and happened upon the following 2 articles:

and

and based on those articles, I chose to go with the scoped belongs_to solution.

Let’s take Ana’s example to illustrate what I mean. Given that we have the following models:

class Booking < ApplicationRecord
belongs_to :bookable, polymorphic: true
end
class Accommodation < ApplicationRecord
has_many :bookings, as: :bookable
end
class Office < ApplicationRecord
has_many :bookings, as: :bookable
end

we can update the Booking model to:

class Booking < ApplicationRecord
belongs_to :bookable, polymorphic: true
belongs_to :accommodation, -> { where(bookings: { bookable_type: 'Accommodation' }) }, foreign_key: 'bookable_id'
belongs_to :office, -> { where(bookings: { bookable_type: 'Office' }) }, foreign_key: 'bookable_id'
end

and now we can create a query that includes a nested join based on the association between Booking and one of the bookable models.

Example:

class User < ApplicationRecord
has_many :bookings
end
User.joins(bookings: [ :office ])
.where(offices: { zip_code: 90210 })

Cool.

However, when implementing this solution, I ran into an issue that was not mentioned in those articles. My tests started failing with an error that looks like this:

ActiveRecord::StatementInvalid:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "bookings"
LINE 1: ...bookings" WHERE "office"."id" = 4484 AND ...
^
# [REDACTED]
# -e:1:in `<main>'
# ------------------
# --- Caused by: ---
# PG::UndefinedTable:
# ERROR: missing FROM-clause entry for table "bookings"
# LINE 1: ...bookings" WHERE "office"."id" = 4484 AND ...
#

One weird thing I noticed was that the error contains references to FactoryBot and wondered what the connection was. It wasn’t obvious at first why my tests were suddenly failing, when they weren’t even referencing this new association or new code.

After staring at the issue for a couple of hours, and trying to track down the cause, I remembered that one thing that tends to cause headaches with belongs_to association is their required nature by default since Rails 5. So, I made a simple adjustment, I added the optional: true option to the association:

class Booking < ApplicationRecord
belongs_to :bookable, polymorphic: true
...
belongs_to :office, -> { where(bookings: { bookable_type: 'Office' }) }, foreign_key: 'bookable_id', optional: true
end

and everything went back to working as usual.

What I figured was that the original code was still creating a Booking record with a bookable association and will never directly reference the office association in the creation/updating process. Which means that marking that association as optional was the right approach.

I hope this helps.

Cheers!

--

--