There are a number of posts and threads on has_many :through, but I haven't found any that cover specifically what I'm trying to do.
I have a User model and a Friendships model. A user has many users that are following them, as well as many followers. It is the usual Twitter model.
For a given user, I want to setup Active Record relationships that return the actual users that are following the user and that the user is a follower of.
These are the relationships that I have setup:
class User < ActiveRecord::Base has_many :following, :class_name => 'User', :through => :friendships, :foreign_key => 'user_id' has_many :followers, :class_name => 'User', :through => :friendships, :foreign_key => 'friend_id' end class Friendship < ActiveRecord::Base belongs_to :user belongs_to :following, :class_name => 'User', :foreign_key => 'friend_id' belongs_to :follower, :class_name => 'User', :foreign_key => 'user_id' end
The Following relationship works - it generates the below join:
SELECT `users`.* FROM `users` INNER JOIN `friendships` ON `users`.id = `friendships`.friend_id WHERE ((`friendships`.user_id = 1))
All is grand.
However, the Follower relationship does not work. I've tried a number of variations, but most seem to return the same set of results as Following.
I need the join to be setup as follows to return the correct result set.
SELECT `users`.* FROM `users` INNER JOIN `friendships` ON `users`.id = `friendships`.user_id WHERE ((`friendships`.friend_id = 1));
Where am I going wrong?
I can set this up using the finder_sql option on the has_many, but it seems like there should be a better way.
has_many :followers, :class_name => 'User', :finder_sql => 'SELECT `users`.* FROM `users` INNER JOIN `friendships` ON `users`.id = `friendships`.user_id WHERE ((`friendships`.friend_id = #{ id }))'
Thanks!
I made a bit of progress and finally got the relationship working by breaking the relationships into two parts, as was shown in this response: Self-referential has_many :through with customized :primary key issue
# FOLLOWING has_many :friendships_to, :foreign_key => 'user_id', :class_name => 'Friendship' has_many :following, :through => :friendships_to, :class_name => 'User' # FOLLOWERS has_many :friendships_from, :foreign_key => 'friend_id', :class_name => 'Friendship' has_many :followers, :through => :friendships_from, :class_name => 'User'
However, while it was possible to have a one-line version of the relationship for following
has_many :following, :class_name => 'User', :through => :friendships, :foreign_key => 'user_id'
I still wasn't able to get it to work for followers. Still wondering how this could be done?
You need to make sure ActiveRecord knows what the source association for the User#friends and likewise the followers and specify the class and foreign_key for the relationships that ActiveRecord can't extrapolate from the association names.
class Following < ActiveRecord::Base belongs_to :user belongs_to :followed, :class_name => 'User' end class User < ActiveRecord::Base has_many :followings has_many :friends, :through => :followings, :source => 'followed' has_many :followeds, :class_name => 'Following', :foreign_key => 'followed_id' has_many :followers, :through => :followeds, :source => :user end
I'm kind of a noob in a learning process, but this models seem cleaner to me:
class User < ActiveRecord::Base has_many :followings has_many :followers, through: :followings end class Following < ActiveRecord::Base belongs_to :user belongs_to :follower, class_name: 'User' end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With