'Create grandchildren associations in the same Rails model
I want to get something like this in one class using ActiveRecord:
grandfather = Person.create(name: "Grandfather")
son = Person.create(name: "Son", parent: grandfather)
grandson = Person.create(name: "Grandson", parent: son)
grandfather.children.map(&:name)
=> ["Son"]
grandfather.grandchildren.map(&:name)
=> ["Grandson"]
I wrote the children association this way:
class Person < ActiveRecord::Base
belongs_to :parent, class_name: "Person", foreign_key: "parent_id"
has_many :children, class_name: "Person", foreign_key: "parent_id"
end
And it works, but I got stuck with grandchildren. Any ideas?
Solution 1:[1]
You can start with something like this:
Models
Person
Child
Grandchild
A Person
has_many :children
A Child
belongs_to :person
A Child
has_many :grandchildren
A Grandchild
belongs_to :child
Till now we haven't established the relationship between Person
and Grandchild
, so we basically need to use Child
relationship to establish this relationship with has_many :through
associations, So:
A Person
has_many :grandchildren, through: :children
A Grandchild
belongs_to: person, through: :children
I have not tested this code
Hope this helps
Solution 2:[2]
I tried to defined by association has_many :grandchildren, through: :children
, but failed. Rails complains SystemStackError: stack level too deep
But you can defined as a method,
def grandchildren
Person.where(parent: children)
end
Solution 3:[3]
I tried this and this works for me to find grand children of a Human.
class Human < ApplicationRecord
has_many :children, :foreign_key => "parent_id" , :class_name => "Human"
belongs_to :parent, :class_name => "Human", optional: true
has_many :grand_children, class_name: :Human, through: :children, source: :children
end
But I couldn't find way to reverse the association to find grand parent of a human. I tried:
belongs_to :grand_parent, class_name: :Human, optional: true
but got nil class.
I fix it by following
class Human < ApplicationRecord
has_many :children, :foreign_key => "parent_id" , :class_name => "Human"
belongs_to :parent, :class_name => "Human", optional: true
has_many :grand_children, class_name: :Human, through: :children, source: :children
has_one :grand_parent, class_name: :Human, through: :parent, source: :parent
end
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | new2cpp |
Solution 3 |