:through alternate

I’d like to use :through to create a web of associations like:

class Thing < ActiveRecord::Base
has_many :child_things, :through => :thing_thing
has_many :parent_things, :through => :thing_thing, :some_other_option?
end

class ThingThing < ActiveRecord::Base
belongs_to :thing
belongs_to :child_thing, :class_name => ‘Thing’, :foreign_key =>
‘child_thing_id’
end

The child_things works. The parent_things does not, or at least, I
cannot figure out the sytnax. I would like it to issue an sql like:

SELECT things.* FROM things INNER JOIN thing_things ON things.id =
thing_things.thing_id WHERE (thing_things.child_thing_id = 6201)

Is there a way to trigger this sql using the through options?

Many thanks

Tom

It looks like you are trying to create a self-referential has_many
:through association, which should work just fine for what you want.
However you don’t have it set up properly. The :through option on a
has_many association must refer to another has_many association in the
same model. It gets to its target through the other association, thus
the name.

Here’s an example snipped from the Rails 1.1 test code (always a good
place to look for example code):

class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, :through => :author_favorites, :order =>
‘name’
end

class AuthorFavorite < ActiveRecord::Base
belongs_to :author
belongs_to :favorite_author, :class_name => “Author”, :foreign_key =>
‘favorite_author_id’
end

If you need to change the name of the association from its name in the
join model, use the :source option to specify that name. Check the docs
on that.

I should blog an article on this soon, as it can get a bit tricky to
combine all the options and get them to work together correctly.


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

Thomas Counsell wrote:

I’d like to use :through to create a web of associations like:

class Thing < ActiveRecord::Base
has_many :child_things, :through => :thing_thing
has_many :parent_things, :through => :thing_thing, :some_other_option?
end

class ThingThing < ActiveRecord::Base
belongs_to :thing
belongs_to :child_thing, :class_name => ‘Thing’, :foreign_key =>
‘child_thing_id’
end

The child_things works. The parent_things does not, or at least, I
cannot figure out the sytnax. I would like it to issue an sql like:

SELECT things.* FROM things INNER JOIN thing_things ON things.id =
thing_things.thing_id WHERE (thing_things.child_thing_id = 6201)

Is there a way to trigger this sql using the through options?

Many thanks

Tom

Thanks Josh.

I didn’t explain myself clearly. In terms of your model, I’m really
looking for the ability to add a reverse link:

class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, :through => :author_favorites, :order =>
‘name’

I’d like the reverse as well:

has_many :other_authors_who_love_me, :through => :author_favourites
end

class AuthorFavorite < ActiveRecord::Base
belongs_to :author
belongs_to :favorite_author, :class_name => “Author”, :foreign_key =>
‘favorite_author_id’
end

The :source option appears to rename the association, what I want is to
have :other_authors_who_love_me return AuthorFavorite#author by joining
on AuthorFavorite#favorite_author.

Is this possible?

Thanks

Tom

Josh S. wrote:

It looks like you are trying to create a self-referential has_many
:through association, which should work just fine for what you want.
However you don’t have it set up properly. The :through option on a
has_many association must refer to another has_many association in the
same model. It gets to its target through the other association, thus
the name.

Here’s an example snipped from the Rails 1.1 test code (always a good
place to look for example code):

class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, :through => :author_favorites, :order =>
‘name’
end

class AuthorFavorite < ActiveRecord::Base
belongs_to :author
belongs_to :favorite_author, :class_name => “Author”, :foreign_key =>
‘favorite_author_id’
end

If you need to change the name of the association from its name in the
join model, use the :source option to specify that name. Check the docs
on that.

I should blog an article on this soon, as it can get a bit tricky to
combine all the options and get them to work together correctly.


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

Thomas Counsell wrote:

I’d like to use :through to create a web of associations like:

class Thing < ActiveRecord::Base
has_many :child_things, :through => :thing_thing
has_many :parent_things, :through => :thing_thing, :some_other_option?
end

class ThingThing < ActiveRecord::Base
belongs_to :thing
belongs_to :child_thing, :class_name => ‘Thing’, :foreign_key =>
‘child_thing_id’
end

The child_things works. The parent_things does not, or at least, I
cannot figure out the sytnax. I would like it to issue an sql like:

SELECT things.* FROM things INNER JOIN thing_things ON things.id =
thing_things.thing_id WHERE (thing_things.child_thing_id = 6201)

Is there a way to trigger this sql using the through options?

Many thanks

Tom

The :source option doesn’t just rename the association, but it chooses
which association in the join model to use to join to the target model.
I think it will do just what you want:

has_many :other_authors_who_love_me, :through => :author_favourites,
:source => :author


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

Thomas Counsell wrote:

Thanks Josh.

I didn’t explain myself clearly. In terms of your model, I’m really
looking for the ability to add a reverse link:

class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, :through => :author_favorites, :order =>
‘name’

I’d like the reverse as well:

has_many :other_authors_who_love_me, :through => :author_favourites
end

class AuthorFavorite < ActiveRecord::Base
belongs_to :author
belongs_to :favorite_author, :class_name => “Author”, :foreign_key =>
‘favorite_author_id’
end

The :source option appears to rename the association, what I want is to
have :other_authors_who_love_me return AuthorFavorite#author by joining
on AuthorFavorite#favorite_author.

Is this possible?

Thanks again Josh, that didn’t do what I want. After much time wasted
the solution was obvious (although a little clunky), so it is probably
me not explaining clearly.

class Author < ActiveRecord::Base

Forwards

has_many :author_favorites
has_many :favorite_authors, :through => :author_favorites, :order =>
‘name’

 # Backwards
has_many :reverse_author_favourites, :class_name => ‘AuthorFavorite’,
:foreign_key => :favorite_author
has_many :other_authors_who_love_me, :through =>
:reverse_author_favourites, :source => :authors
end

class AuthorFavorite < ActiveRecord::Base
belongs_to :author
belongs_to :favorite_author, :class_name => “Author”, :foreign_key =>
‘favorite_author_id’
end

Josh S. wrote:

The :source option doesn’t just rename the association, but it chooses
which association in the join model to use to join to the target model.
I think it will do just what you want:

has_many :other_authors_who_love_me, :through => :author_favourites,
:source => :author