Reputation: 4566
say I have the following STI models
class Parent < ActiveRecord::Base
has_many :childs, foreign_key: 'parent_id' # forgive the english for sake of simplicity
#def childs
# Child.where(parent_id: id) # this works just fine BTW
#end
end
class BadParent < Parent
end
class GoodParent < Parent
end
and the following Child class
class Child
belongs_to :parent # parent_id lives on this
end
I dont care about setting the type on the Child so I don't care about creating a polymorphic association.
bad_parent = BadParent.create(name: 'Michael Jackson')
child = Child.create(name: 'Bobby', parent: bad_parent)
If I run
child.parent #=> <# BadParent > # AWESOME
bad_parent.childs #=> [] NO BUENO!!!
sql_statement = bad_parent.childs.to_sql #=> "SELECT `childs`.* FROM `childs` WHERE `childs`.`parent_id` = 1"
Child.find_by_sql(sql_statement) #=> [<# Child Object #>] BUENO!!!
Is there something I have to add to the association to make this work like find_by_sql
?
Upvotes: 1
Views: 2042
Reputation: 76774
Here's what I'd do...
#app/models/person.rb
class Person < ActiveRecord::Base
#columns id | type | parent_id | type_id | name | created_at | updated_at
end
#app/models/parent.rb
class Parent < Person
belongs_to :type
has_many :children, foreign_key: 'parent_id'
end
#app/models/child.rb
class Child < Parent
belongs_to :parent
end
#app/models/type.rb
class Type < ActiveRecord::Base
#columns id | type
#values good/bad (probably overkill to have DB for this)
has_many :parents
end
This should allow you to call the following:
@parent = Parent.find params[:id]
@parent.children #-> collection of Person model with parent_id attribute for parent
In regards your specific issue - about eager loading
etc - I don't have masses of experience.
There are hierarchy gems which help this out.
We've used ClosureTree
before - it creates a separate table which allows you to traverse hierarchies much easier. Another one is called ancestry
which is pretty popular (easier to implement).
I'd recommend using the likes of ancestry
in your "parent" model. I called the parent model Parent
because I think it will give you much deeper scope to work with different types of data.
For example, your Child
model is its own entirely, when it should be made up of the same data as the Parent
.
Upvotes: 1
Reputation: 4676
As per other comments, you shouldn't have both a method and association named the same as it is very unclear what will get executed - I'll assume for here on you will get rid of def childs ...
Aside from that I think your issue is to do with caching i.e. rails will only hit the DB if it knows something has changed. In your example bad_parent doesn't know that new children have been added. You could either reload like:
bad_parent.reload
bad_parent.childs #> should show child object now
or force a call to the DB like:
bad_parent.childs(true)
Check out 3.1 Controlling Caching section of the rials guides for more info: http://guides.rubyonrails.org/association_basics.html
Upvotes: 3