So in chapter 12 or Hartl's Railstutorial, we're building the ability for users to follow one another's "twitter" feeds. This is modeled as users forming relationships and we create a relationship model with a table that has a follower_id and a followed_id. Also in the model, we associate it with the user model as follows:
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
end
We also associate the user model with the relationship model as follows:
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: :followed
has_many :followers, through: :passive_relationships, source: :follower
I'm confused as to why we need to have has_many :following in the user model. It says in the tutorial that following someone is an active relationship, so why do we need to say users have many active relationships, and users are also following many (which is an active relationship). What exactly is has_many :following doing that has_many :active_relationships cannot do?
Also my second question is why the belongs_to is split into follower and followed, instead of just user. What do we gain by using two belongs_to instead of just one on the user?
It is a way to access the Users that are either following or being followed by a particular user instead of the relationships.
If you just had #user.active_relationships that would return back the relationships in the join table. But with #user.following you get an association array of User objects.
And as for your second question, a relationship between two users takes 2 objects not one, and would be pointless to have just a single belongs_to :user.
Ruby on Rails Guides - Associations | Has many :through
Related
I have User model in my database. It has role. role can be either patient or doctor. I want to maintain doctor_patient table.
doctor_patient.rb
belongs_to :doctor, class_name: 'User'
belongs_to :patient, class_name: 'User'
a patient can belong to many doctors and a docor can have many patients. I am familier to regular or normal has_many through association but facing issues related to this scenarios where I have role in user model.
user.rb
user
has_many :doctor_patients
has_many :patients, :through => :doctor_patients, :class_name=> "User"
patient
has_many :doctor_patients
has_many :doctors, :through=> :doctor_patients, :class_name=> "User"
In ActiveRecord the assocation metadata (the reflection) is strored in a class attribute as a hash and the name is used as the key. So when you define multiple assocations with the same name you're just overwriting your previous assocation.
The solution is to use unique names for each assocation:
class User < ApplicationController
has_many :doctor_patients_as_doctor,
foreign_key: :doctor_id,
class_name: 'DoctorPatient'
has_many :patients,
through: :doctor_patients_as_doctor
has_many :doctor_patients_as_patient,
foreign_key: :patient_id,
class_name: 'DoctorPatient'
has_many :doctors,
through: :doctor_patients_as_patient
end
Also make sure you pluralize the table correctly and name it doctor_patients.
So I am working with the Ruby ORM and trying to understand many-to-many syntax and polymorphism.
Here are my Active Record relationships so far.
class Association < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: "User"
end
and
class User < ActiveRecord::Base
has_many :associations
has_many :friends, through: :associations
end
I can't seem to get a list of friends per user when those friends are associated with more than one user. In other words some users have friends and these friends may have more than one user association too.
First off, these are not polymorphic associations. One uses polymorphic associations when a model could belong to many models, like a Comment model. A user can comment on a post, on a picture, on a project, so the Comment model could belong to any of these, so there we use Polymorphic associations. Read here to know more about it.
Well, the thing that you are asking is about Inverse Friends, and here is how you can implement it.
class User < ActiveRecord::Base
has_many :associations
has_many :friends, through: :associations
has_many :inverse_associations, class_name: "Association", foreign_key: :friend_id
has_many :inverse_friends, through: :inverse_associations, source: :user
end
class Assocation < ActiveRecord::Base
belongs_to :user
belogns_to :friend, class_name: 'User'
belongs_to :inverse_friend, class_name: 'User', foreign_key: :friend_id
end
I'm having problems with a Rails 4 join table. I have quite a simple setup which is working elsewhere in my application using a non-conventionally named table for users, groups and usergroupmemberships. I'm trying to set it up this time using the proper conventional naming and it's just not working.
Models involved are User, ManagementGroup and ManagementGroupsUser
db tables: management_groups_user, management_groups, users
app/models/user.rb
Class User < ActiveRecord::Base
...
has_many :management_groups, through: management_groups_users
has_many :management_groups_users
....
app/models/management_group.rb
class ManagementGroup < ActiveRecord::Base
has_many :users, through: :management_groups_users
has_many :management_groups_users
app/models/management_groups_user.rb
class ManagementGroupsUser < ActiveRecord::Base
belongs_to :users
belongs_to :management_groups
The association appears to work from with #user.management_groups_users but nothing else. I'm fairly sure this is a problem with naming / plurality but I can't figure it out.
This is the model which joins the remaining models user.rb and management_group
#app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
Since we are going to use model above to access another model management_group then
#app/models/user.rb
has_many :user_management_groups #This should come first
has_many :management_groups, through: user_management_groups
Since we are going to use model above to access another user model then
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
Now should work
Do it this way.
app/models/user.rb
has_many :user_management_groups
has_many :management_groups, through: user_management_groups
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
I hope these associations will help you.
This is another way if you pass foreign key and class name.
app/models/user.rb
has_many :user_management_groups, :foreign_key => "key", :class_name => "ClassName"
has_many :management_groups, through: user_management_groups, :foreign_key => "key", :class_name => "ClassName"
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user, class_name: "ClassName"
belongs_to :management_group, class_name: "ClassName"
This is another way around.
It's important to realize there is a convention rails uses for HABTM and has many through. HABTM does not have a model, so it needs to infer the table name which, as others point out, is both plural and alphabetical order.
If you are doing has many through and have a model, the convention is that it wants singular first word, plural second. See examples
User has and belongs to many groups.
HABTM: table name should be groups_users
Has Many Through: table name should be user_groups (flip order is more intuitive)
Model for the latter would be UserGroup. Has many through would specify it as through: :user_groups
I'm have a fairly standard business model with Account instances who can follow each other. It's just like Twitter. The join model is called Relationship.
class Account
has_many :active_relationships, class_name: 'Relationship',
dependent: :destroy, foreign_key: :follower_id
has_many :passive_relationships, class_name: 'Relationship',
dependent: :destroy, foreign_key: :followed_id
# These are accounts that a given account is following
has_many :friends, through: :active_relationships, source: :followed
# These are accounts that follow the given account
has_many :followers, through: :passive_relationships, source: :follower
end
class Relationship
belongs_to :follower, class_name: 'Account'
belongs_to :followed, class_name: 'Account'
end
I'd like to scope to twitter accounts which follow two or more other specific accounts.
For example, imagine four accounts exist in the database. They're named Alan, Becca, Charlie and Dave. Dave follows both Alan and Charlie. Becca only follows Alan. When I apply my scope it should return Dave only.
I can scope to users who follow one specific account like this:
Account.includes(:active_relationships)
.where(relationships: { followed_id: alan.id })
but chaining where clauses returns zero results because the DB looks for a relationship record where the followed_id is simultaneously equal to two different account ids. This is obviously impossible.
Account.includes(:active_relationships)
.where(relationships: { followed_id: alan.id })
.where(relationships: { followed_id: charlie.id })
How do I implement this correctly with ActiveRecord?
Rails documentation provides a nice explanation of how to handle a self join where only a has_many-belongs_to relationship is required. In the example, an employee (as a manager) can have many employees (each, as a subordinate).
However, how do you handle a has_many-has_many self join (which I've heard referred to as a bi-directional looped association)?
For example, how do you handle the situation in which an employee can have many subordinates, in its capacity as manager, and also have many managers, in its capacity as subordinate?
Or, in other words, where a user can follow many users and be followed by many users?
A User can have many:
followers in its capacity as followee
followees in its capacity as follower.
Here's how the code for user.rb might look:
class User < ActiveRecord::Base
# follower_follows "names" the Follow join table for accessing through the follower association
has_many :follower_follows, foreign_key: :followee_id, class_name: "Follow"
# source: :follower matches with the belong_to :follower identification in the Follow model
has_many :followers, through: :follower_follows, source: :follower
# followee_follows "names" the Follow join table for accessing through the followee association
has_many :followee_follows, foreign_key: :follower_id, class_name: "Follow"
# source: :followee matches with the belong_to :followee identification in the Follow model
has_many :followees, through: :followee_follows, source: :followee
end
Here's how the code for follow.rb:
class Follow < ActiveRecord::Base
belongs_to :follower, foreign_key: "follower_id", class_name: "User"
belongs_to :followee, foreign_key: "followee_id", class_name: "User"
end
The most important things to note are probably the terms :follower_follows and :followee_follows in user.rb. To use a run of the mill (non-looped) association as an example, a Team may have many :players through :contracts. This is no different for a Player, who may have many :teams through :contracts as well (over the course of such Player's career).
But in this case, where only one named model exists (i.e. a User), naming the through: relationship identically (e.g. through: :follow) would result in a naming collision for different use cases of (or access points into) the join table. :follower_follows and :followee_follows were created to avoid such a naming collision.
Now, a User can have many :followers through :follower_follows and many :followees through :followee_follows:
To determine a User’s :followees (upon an #user.followees call to the database), Rails may now look at each instance of class_name: “Follow” where such User is the the follower (i.e. foreign_key: :follower_id) through: such User’s :followee_follows.
To determine a User’s :followers (upon an #user.followers call to the database), Rails may now look at each instance of class_name: “Follow” where such User is the the followee (i.e. foreign_key: :followee_id) through: such User’s :follower_follows.