Reputation: 1743
So, I have a system where users are able to follow authors (other users).
User Model:
class User < ActiveRecord::Base
has_many :author_following, class_name: 'Following'
has_many :following, through: :author_following, source: :author
has_many :followers, foreign_key: 'author_id', through: :author_following, source: :user
end
Following Model:
class Following < ActiveRecord::Base
belongs_to :user
belongs_to :author, foreign_key: 'author_id', class_name: "User"
end
Issue: I am able to get the list of authors that i am following, but I am able to get the list of my followers.
Given: u
is a valid user that is following others and has followers
u.following
generates the following SQL:
SELECT "users".* FROM "users" INNER JOIN "followings" ON "users"."id" = "followings"."author_id" WHERE "followings"."user_id" = $1 [["user_id", 1]]
Which is correct..
u.followers
generates the following SQL:
SELECT "users".* FROM "users" INNER JOIN "followings" ON "users"."id" = "followings"."user_id" WHERE "followings"."user_id" = $1 [["user_id", 1]]
Which is wrong..
Ideally this SQL would be WHERE "followings"."author_id" = $1
Upvotes: 2
Views: 848
Reputation: 962
@Billy Chan's answer above is close, but you also need to specify the other side of relationship as well with "association_foreign_key", and switch follower_id with followee_id on our side. Also, join table is users_users actually.
class User < ActiveRecord::Base
has_and_belongs_to_many :followers, class_name: 'User',
foreign_key: 'followee_id', association_foreign_key: 'follower_id'
has_and_belongs_to_many :followees, class_name: 'User',
foreign_key: 'follower_id', association_foreign_key: 'followee_id'
end
# Migration
create_table :users_users do |t|
t.belongs_to :followee
t.belongs_to :follower
end
Now User.followers and User.followees work as expected
Upvotes: 0
Reputation: 24815
Another way is to use has_and_belongs_to_many
. No second model needed.
class User < ActiveRecord::Base
has_and_belongs_to_many :followers, class_name: 'User', foreign_key: 'follower_id'
has_and_belongs_to_many :followees, class_name: 'User', foreign_key: 'followee_id'
end
# Migration
create_table :followees_followers do |t|
t.belongs_to :followee
t.belongs_to :follower
end
This is simpler, but the validation part(say verifying somebody is an author) need to be done in User model
Upvotes: 1
Reputation: 1743
Of course, I figure it your right after posting the question. However if you think there is a more elegant way of doing this, please comment :)
To solve, I changed:
User Model:
class User < ActiveRecord::Base
has_many :author_following, class_name: 'Following'
has_many :following, through: :author_following, source: :author
has_many :author_followers, foreign_key: 'author_id', class_name: 'Following'
has_many :followers, through: :author_followers, source: :user
end
Following Model:
class Following < ActiveRecord::Base
belongs_to :user
belongs_to :author, class_name: "User"
end
Upvotes: 4