Reputation: 370
I have a relationship of a Parent, Child, Skills. Each Parent can have several children and each child has one skill.
I want to return all Parents from the database and include the children that belong to them if they have a certain skill.
At the end of the query we will have several parents with possibly 0 children. I'm trying something like this Parent.includes(children: [:skill]).where(skill: {skill_type: type})
but this doesn't return all the Parents. Is this possible to do through ActiveRecord?
Upvotes: 1
Views: 1338
Reputation: 101811
class Parent < ActiveRecord::Base
has_many :children
has_many :skills, through: :children
def self.with_skill(skill_type)
children = Child.joins(:skills).where(skills: { skill_type: skill_type } )
Parent.all.map do |parent|
children = children.select { |c| c.parent_id == parent.id }
# This marks the association as loaded so that rails does not issue a n+1 query
association = parent.association(:children)
association.loaded!
association.target.concat(children)
children.each { |c| association.set_inverse_instance(c) }
parent.readonly! # because we dont want to accidentally set the children to []
parent
end
end
end
Here we use two queries, the first fetches all the children with the selected skill and the skill. The second gets all the parents.
We then manually setup the relation between Parent and Child so that it parent.children
does not cause ActiveRecord to query the database for children.
We also mark the record as read-only since if one of these records was saved it could remove the parent_id
from the associated children.
All in all this is a bit of a work-around which will work well for displaying the records. Rails does not really let you pick and choose exactly what associations should be preloaded in the way you would desire here.
Upvotes: 1