I don't get something in the last chapter of the Rails tutorial.
So the aim of this chapter to make friendships with other users, and that makes it a self referential association. (users have a relationship with other users)
So with the User model, there is the Friendship model, that acts as a through table.
And in the code, class User
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
.
.
.
end
But I don't get this part:
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
We have to specify in the has_many :through association the table that we are going through (Relationship table). But in the above code there isn't an
:active_relationships or :passive_relationships table ,there's only a Relationship class.
The Relationship table:
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
So, my question is, how does that work?
Tnx Tom
You are right you have just Relationship class.
In rails by default there will be has_namy :relationships then you don't have to specify the class name.
If you don't follow the rails default rules, then when you will try to with different association name , you have to specify the class name.
In your example
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
Here you specified to find active relationships from Relationship class.
The has_many :through refers to an association.
has_many :following, through: :active_relationships, source: :followed
The has_many :through refers to an association, not a table. The :source is a relationship in the class that that association refers to.
In this case
has_many :followers, through: :passive_relationships, source: :follower
refers to this relationship
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
And in the relationship class, there is a :follower that is the actual source for this object.
Related
i'm working on follow and following functionality with help of this article https://www.railstutorial.org/book/following_users
It's working fine but i added extra column with name status as a boolean which present the follow request has been accepted. current_user.following return all relations of current_user but i need those users whose relationship status is true.
My code is totally same as mention article , only i added status column in relationship table.
So kindly help me
I don't know the original code, but intuitively this should work
current_user.following.where(status: true)
As https://www.railstutorial.org/book/following_users:
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
.
.
.
end
So, I think you can do like below to solve your problem:
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
has_many :active_relationships, ->{ where(status: true) },
class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
.
.
.
end
Hope it helps.
I'm quite stuck in in Ruby on Rails relations and I really appreciate you help.
Have model User
class User < ActiveRecord::Base
has_many :followers, :through => :follows, foreign_key: "followee_id"
has_many :followees, :through => :follows, foreign_key: "follower_id"
end
and model Follow
class Follow < ActiveRecord::Base
belongs_to :followee, class_name: "User"
belongs_to :follower, class_name: "User"
end
but if want to create new follower like:
user.followers << User.first
the result is SystemStackError
Thank you for every help!
You have to try something like this:
class User < ActiveRecord::Base
has_many :follower_follows, foreign_key: :followee_id, class_name: "Follow"
has_many :followers, through: :follower_follows, source: :follower
has_many :followee_follows, foreign_key: :follower_id, class_name: "Follow"
has_many :followees, through: :followee_follows, source: :followee
end
Here follower_follows and followee_follows are join tables and source: :follower matches with the belong_to :follower identification in the Follow model and source: :followee matches with the belong_to :followee identification in the Follow model
I think this would work in your case
I am following Michael Hartl's rails book and am getting the following error message:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :follower in model Relationship. Try 'has_many :following, :through => :active_relationships, :source => <name>'. Is it one of ?
when calling michael.follower (where michael is a user object).
Here are the associations:
class User < ActiveRecord::Base
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, :through => :active_relationships, source: :follower
has_many :followers, :through => :passive_relationships, source: :followed
end
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User", foreign_key: "follower_id"
belongs_to :followed, class_name: "User", foregin_key: "followed_id"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
If I understand correctly, you have many-to-many relation between User and Relationship models through relationship-model (I'm sorry for tautology).
I suggest to change
has_many :following, :through => :active_relationships, source: :follower
has_many :followers, :through => :passive_relationships, source: :followed
to
has_many :following, :through => :active_relationships, class_name: "User"
has_many :followers, :through => :passive_relationships, class_name: "User"
How to write this:
has_many :sales, foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :sales, source: :book
as this:
has_many :purchases, class_name: 'Sale', foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :sales, source: :book
It gives me the following error at the moment:
Could not find the association :sales in model User (ActiveRecord::HasManyThroughAssociationNotFoundError)
And writing it has_many :sales is grammatically incorrect when foreign_key: :buyer_id.
The :through key must reference an association that is defined.
has_many :purchases, class_name: 'Sale', foreign_key: :buyer_id, dependent: :destroy
has_many :purchased_books, class_name: 'Book', through: :purchases, source: :book
I'd like to set up multiple has_many :through relationships in parallel. Here are my 2 standard and 2 join models:
User.rb
has_many :ownerships, dependent: :destroy
has_many :devices, through: :ownerships
has_many :bookings, dependent: :destroy
has_many :devices, through: :bookings
Ownership.rb
belongs_to :user, touch: true, counter_cache: :devices_count
belongs_to :device, touch: true
Booking.rb
belongs_to :user, touch: true, counter_cache: :bookings_count
belongs_to :device, touch: true, counter_cache: :bookings_count
Device.rb
has_many :ownerships, dependent: :destroy
has_many :users, through: :ownerships
has_many :bookings, dependent: :destroy
has_many :users, through: :bookings
This current setup is NOT working as expected, there seems to be crosstalk between the join models. I want the join models to be independent and in parallel (i.e. Users can have relationships - Ownerships - with devices independently of being able to book them). I am not looking for a nested has_many :through relation here.
When I change the User Ownerships of a Device that seems to alter the number of Bookings and vice versa... any ideas on how should I be setting this up correctly?
I think the first error you've got is you're calling two associations by the same name (users / devices)
To help any further respondents, the real question is --> how do you set up an association to pull from multiple join models?
Quick Fix
Rails associations are named primarily by their class, but because of conflicts, you should refrain from setting them twice. This is why you're seeing the current issue. A simple resolution will be to call the associations by different names:
User.rb
has_many :ownerships, dependent: :destroy
has_many :owner_devices, through: :ownerships, class_name: "Device", foreign_key: "ownership_id"
has_many :bookings, dependent: :destroy
has_many :booking_devices, through: :ownerships, class_name: "Device", foreign_key: "booking_id"
I am still looking for information on how you could set an association to use two models
This appears be a working solution following Rich Peck's suggestions:
User.rb
has_many :ownerships, dependent: :destroy
has_many :device_ownerships, through: :ownerships, class_name: "Device", foreign_key: "device_id", source: :device
has_many :bookings, dependent: :destroy
has_many :device_bookings, through: :bookings, class_name: "Device", foreign_key: "device_id", source: :device
Booking.rb (Join model)
belongs_to :user, touch: true, counter_cache: :bookings_count
belongs_to :device, touch: true, counter_cache: :bookings_count
Ownership.rb (Join model)
belongs_to :user, touch: true, counter_cache: :devices_count
belongs_to :device, touch: true, counter_cache: :users_count
Device.rb
has_many :ownerships, dependent: :destroy
has_many :user_ownerships, through: :ownerships, class_name: "User", foreign_key: "user_id", source: :user
has_many :bookings, dependent: :destroy
has_many :user_bookings, through: :bookings, class_name: "User", foreign_key: "user_id", source: :user
To be honest, I'm a bit confused over why the foreign_key's need(?) to be set up as they are, so I'll have to do a bit more reading about it. Otherwise it appears to be functional, I don't see crosstalk between these join models anymore.