Nested has_many :through

Hi folks,

I’m using rails-1.2.2 and have run into unexpected behavior with
has_many :through.

I get a SQL exception reporting the use of an unknown column when I
use has_many :through to navigate thusly: Project → BacklogItems →
BacklogTask → TimeEstimates.

It looks like rails omits a required join and somehow comes to believe
that it can navigate from a BacklogTask directly to a product.

The following code is available at Parked at Loopia for
those of you who prefer syntax-highlighting:


Product.find(:first).hours() generates this error:

#Exception: SQLite3::SQLException: no such column:
backlog_tasks.product_id: SELECT time_estimates.* FROM time_estimates
INNER JOIN backlog_tasks ON time_estimates.backlog_task_id =
backlog_tasks.id WHERE ((backlog_tasks.product_id = 1))

class Product < ActiveRecord::Base
has_many :backlog_items, :dependent => :destroy,
:order => :name
has_many :backlog_tasks, :through => :backlog_items
has_many :time_estimates, :through => :backlog_tasks

def hours(at = Time.now)
time_estimates.sum(:hours, :conditions =>
[“time_estimates.created_at <= ?”, at])
end
end

class BacklogItem < ActiveRecord::Base
belongs_to :product
has_many :backlog_tasks, :dependent => :destroy,
:order => :name
has_many :time_estimates, :through => :backlog_tasks
end

class BacklogTask < ActiveRecord::Base
belongs_to :backlog_item
has_many :time_estimates, :dependent => :destroy,
:order => :created_at
end

class TimeEstimate < ActiveRecord::Base
belongs_to :backlog_task
end

It doesn’t seem to be adapter-specific; I tried with PostgreSQL and
had the same problem, namely that rails appears to omit a required
join.

My guess is that you just can’t use has_many :through this way? I’m
hoping I’ve just missed something that’ll be obvious to fresh eyes.

Ciao,
Sheldon.

Sheldon H. wrote:

class Product < ActiveRecord::Base
has_many :backlog_items, :dependent => :destroy,
:order => :name
has_many :backlog_tasks, :through => :backlog_items
has_many :time_estimates, :through => :backlog_tasks


My guess is that you just can’t use has_many :through this way? I’m
hoping I’ve just missed something that’ll be obvious to fresh eyes.

You guessed right. has_many :through only goes through one level of
join model. But you can fake it in several ways, either with custom SQL
or by navigating the joins in ruby code.


Josh S.
http://blog.hasmanythrough.com

On Feb 13, 5:44 am, Josh S. [email protected]
wrote:

You guessed right. has_many :through only goes through one level of
join model. But you can fake it in several ways, either with custom SQL
or by navigating the joins in ruby code.

Interestingly, there’s been a patch for this in Trac for months, but
it hasn’t been imported because it doesn’t come with unit tests.

Honestly, why the devs insist on tests in this particular case is
beyond me. Smells like dogma winning out over pragmatism.

Anyway, for the benefit of others who run into this, you can grab a
patch at:

http://dev.rubyonrails.org/ticket/6461

Thanks, Josh.

Ciao,
Sheldon.