1

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?

daino3
  • 4,386
  • 37
  • 48
  • I'm not sure I understand. You have the #childs method and also the has_many :childs association. Which one are you trying to use? – Brad Pauly Oct 07 '15 at 21:27
  • Well for starters you should use the proper plural form `children` to avoid errors. – max Oct 07 '15 at 23:15

2 Answers2

3

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

mahi-man
  • 4,326
  • 2
  • 25
  • 36
  • Yeah, weird. I am aware of rails caching db requests, but chalked it up to something with STI weirdness. It seems to work for me now with `#childs(true)` – daino3 Oct 08 '15 at 14:40
1

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.

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • Yeah, I like that alternative you suggest, but am wed to the current implementation (coded up already but will probably create an issue for it). +1 for suggesting `ancestry` and `closureTree` gems. I hadn't thought of those before now. Seems like my real issue is with AR db caching getting confused with STI. Thanks for your help! – daino3 Oct 08 '15 at 14:46