Bad combo in ActiveRecord: polymorphic relationships, auto-generated numeric IDs and `includes`
Navigating the complexities of polymorphic relationships, auto-generated numeric IDs, and the use of “includes” in queries can indeed turn into a wild party in my model. The particular challenge I encountered involves a model called Selection
with two polymorphic associations: bundle
and product
Here’s the snippet that caused the conundrum:
class Selection < ApplicationRecord
belongs_to :bundle,
-> { includes(:selections).where(selections: { selectionable_type: "Bundle" }) },
foreign_key: "selectionable_id",
inverse_of: :selections,
optional: true
belongs_to :product,
-> { includes(:selections).where(selections: { selectionable_type: "Product" }) },
foreign_key: "selectionable_id",
inverse_of: :selections,
optional: true
end
The issue surfaces when I attempt a query like Selection.joins(:bundle)
and encounter an unexpected LEFT OUTER JOIN
SELECT
`selections`.*
FROM
`selections`
INNER JOIN
`bundles` ON `bundles`.`id` = `selections`.`selectionable_id`
LEFT OUTER JOIN
`selections` `selections_bundles` ON
`selections_bundles`.`bundle_id` = `bundles`.`id` AND
`selections`.`selectionable_type` = 'Bundle'
The culprit lies in the belongs_to
declarations where I’ve used includes(:bundle)
To resolve this and ensure an INNER JOIN
in my queries, I replace the includes(:bundle)
with joins(:bundle)
:
class Selection < ApplicationRecord
belongs_to :bundle,
-> { joins(:selections).where(selections: { selectionable_type: "Bundle" }) },
foreign_key: "selectionable_id",
inverse_of: :selections,
optional: true
belongs_to :product,
-> { joins(:selections).where(selections: { selectionable_type: "Product" }) },
foreign_key: "selectionable_id",
inverse_of: :selections,
optional: true
end
With this modification, my queries, like Selection.joins(:bundle)
, will now generate the desired INNER JOIN
:
SELECT
`selections`.*
FROM
`selections`
INNER JOIN
`bundles` ON `bundles`.`id` = `selections`.`selectionable_id`
INNER JOIN
`selections` `selections_bundles` ON
`selections_bundles`.`bundle_id` = `bundles`.`id`
AND `selections`.`selectionable_type` = 'Bundle'
Now, my model’s party with polymorphic relationships, auto-generated IDs, and queries should be a bit more well-behaved. Cheers to smoother database interactions!