Expected result in rails console, grandfather.grandchildren
and grandmother.grandchildren
should return the same group of objects:
grandfather = User.first grandfather.grandchildren => #<ActiveRecord::Associations::CollectionProxy [#<User id: 5, name: "father's son", father_id: 3, mother_id: nil>, #<User id: 6, name: "uncle's son", father_id: 4, mother_id: nil>]> grandmother = User.find(2) grandmother.grandchildren => #<ActiveRecord::Associations::CollectionProxy [#<User id: 5, name: "father's son", father_id: 3, mother_id: nil>, #<User id: 6, name: "uncle's son", father_id: 4, mother_id: nil>]>
This is my association now in User.rb model.
has_many :children, ->(user) { unscope(:where).where("father_id = :id OR mother_id = :id", id: user.id) }, class_name: "User" has_many :grandchildren, through: :children, source: :children belongs_to :mother, class_name: "User", optional: true belongs_to :father, class_name: "User", optional: true
Output in rails console now:
irb(main):001:0> grandfather = User.first (0.3ms) SELECT sqlite_version(*) User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "grandfather", mother_id: nil, father_id: nil> irb(main):002:0> grandfather.grandchildren User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "users" "children_grandchildren" ON "users"."user_id" = "children_grandchildren"."id" WHERE (father_id = 1 OR mother_id = 1) /* loading for inspect */ LIMIT ? [["LIMIT", 11]] Traceback (most recent call last): ActiveRecord::StatementInvalid (SQLite3::SQLException: ambiguous column name: father_id)
Advertisement
Answer
You can’t get the grandchildren from a grandparent by going through its children because it implies that their father/mother ids are equal to the grandparent, it doesn’t travel through the grandchildren parents:
SELECT "users".* FROM "users" INNER JOIN "users" "children_grandchildren" ON "users"."user_id" = "children_grandchildren"."id" WHERE (father_id = 1 OR mother_id = 1) -- this is the grandparent id, when it should be the child parent's id
You can add a callable to the grandchildren relationship, similar to the one for children
, but this time extracting the grandparent children ids, and using the IN clause to filter those user rows matching those ids, with their father/mother ids:
has_many :grandchildren, ->(user) { unscope(:where).where('father_id IN (:ids) OR mother_id IN (:ids)', ids: user.children.ids) }, class_name: "User"