Has_many via self-relational association

I want (as an example) to create an association has_many

to all posts of a person's friends, something like has_many :remote_posts

, to give me something like person > friends > person > posts

.

.. this is how i will do it

script/generate model post title:string person_id:integer
script/generate model friendship person_id:integer friend_id:integer
script/generate model person name:string

      


class Person < ActiveRecord::Base
  has_many :posts
  has_many :friendships, :foreign_key => 'friend_id'
  has_many :people, :through => :friendships
  has_many :remote_posts, :class_name => 'Post', :through => :people, :source => :posts
end
class Friendship < ActiveRecord::Base
  belongs_to :person
  #also has a 'friend_id' to see who the friendship is aimed at
end
class Post < ActiveRecord::Base
  belongs_to :person
end

      


# generate some people and friends
{'frank' => ['bob','phil'], 'bob' => ['phil']}.each {|k,v|
  v.each {|f| 
    Friendship.create(
      :person_id => Person.find_or_create_by_name(f).id,
      :friend_id => Person.find_or_create_by_name(k).id
    )
  }
}
# generate some posts
Person.all.each {|p|
  p.posts.create({:title => "Post by #{p.name}"})
}

      


Now,

Person.first.friendships  # ..works
Person.first.people  # (friends) ..works
Person.first.posts # ..works
Person.first.remote_posts #....

      

... and I am getting this error.

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: people.person_id: SELECT "posts".* FROM "posts" INNER JOIN "people" ON "posts".person_id = "people".id WHERE (("people".person_id = 1))

Aside from the foreign key error - it looks like the friends association is not coming into play at all. I thought it might be because of that :source => :posts

, since the association posts

would enter it twice.

I could write some kind of finder sql (and this is what I am currently working on), although I would rather do it this way.

Any ideas on how to get this to work?

+2


source to share


2 answers


How about this:

In the class FriendShip

add:

has_many :posts, :through => :person

      




and in the class Person

change remote_posts to:

has_many :remote_posts, :class_name => 'Post',
         :through => :friendships, :source => :person

      

+1


source


How about nested relationships has_many :through

. This seems to work for me:

class Friendship < ActiveRecord::Base
  belongs_to :person
  belongs_to :friend, :class_name => 'Person'
  has_many :posts, :through => :friend, :source => :posts
end
class Person < ActiveRecord::Base
  has_many :posts
  has_many :friendships, :foreign_key => 'friend_id'
  has_many :people, :through => :friendships
  has_many :remote_posts, :through => :friendships, :source => :posts
end

      



Note: This requires a nested_has_many_through

plugin
. (Note: direct linking to github repos seems broken ... but this repo is, despite the error message.)

0


source







All Articles