daino3
daino3

Reputation: 4566

STI has_many relationship in parent class inherited by sub classes

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

Answers (2)

Richard Peck
Richard Peck

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

mahi-man
mahi-man

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

Related Questions