An association is a connection between two Active Record models that makes common operations simpler and easier to code. Associations are implemented using macro-style calls, so that you can declaratively add features to your models.
Two important assumptions are made when you create an association:
- The class of the model your association points to is based directly off of the name of the association
- The foreign key in any belongs_to relationship will be called yourassociationname_id.
If you deviate from these defaults, then you need to let Ruby know what class and foreign key to look for.
Here is an example of the assumptions made:
#app/models/message.rb Class Message < ActiveRecord::Base belongs_to :user End
→ The implied promises that we are making through these associations are:
- The class for the belongs_to association is named class User (that is, based off of the name of the association)
- The foreign key in the belongs_to relationship will be called user_id (that is, the messages table includes a user_id -foreign key- column)
#app/models/user.rb Class User < ActiveRecord::Base Has_many :messages end
→ The implied promises that we are making through this association is that:
- The class for the association is named Message (that is, based off of the name of the association)
Through this scenario, you could call User.first.messages and get all the messages from the first user. How? Ruby looks for the user_id (foreign key) in the Messages table and returns all the messages with the user_id of the first User.
Now, what if we’d like to deviate from this implied promise?
For example what if we are creating a social media app and we would like our users to be able to follow other users?
Ok so first of all we would need a following_users table with at least two columns: following_id and follower_id. These columns will allow us to keep track of all user followers and followings. But how do we set up the associations in our classes so that when I start following someone, that someone shows that I am following her/him?
First, we need a class FollowingUser that inherits from ActiveRecord::Base and has the following associations:
Note that instances of this class would be the rows of our following_users table, which is our join table.
Class FollowingUser < ActiveRecord::Base Belongs_to :follower, foreign_key: ‘follower_id’, class_name: ‘User’ Belongs_to :following, foreign_key: ‘following_id’, class_name: ‘User’ end
So here we specify the foreign key and class name so that Ruby knows that we want to deviate from the implied promise. We are telling Ruby to look for the class User to build the association and to use follower_id and following_id as the foreign key to match up against user_id (in the User class) to build the association.
What would be the implied promise if we wouldn't have included a specific foreign key and class_name?
If we didn't include a foreign_key, and class_name, then Ruby would assume that we have a class called Follower and Following and that the foreign key in FollowingUser that we want to use is follower_id and following_id.
Once that is done, we would need to add associations to the User model. Now, keep in mind that we are building a relationship between the same type of model - users can follow other users. This means that we will need to specify both associations in our User model, but name them differently - the implication here is that we will need to specify in our has_many association what the name of the foreign_key will be.
has_many :following_users, foreign_key: 'follower_id' has_many :follower_users, foreign_key: 'following_id', class_name: 'FollowingUser' has_many :following, through: :following_users, foreign_key: 'following_id', class_name: 'User' has_many :followers, through: :follower_users, foreign_key: 'follower_id', class_name: 'User'
Here's a line by line explanation of the code shown above:
Line 1: Here we are saying that a user has_many :following_users (implied promise → there is a class named following_users that we will use to build this association, new explicit instruction → use the following_id column as the foreign key to set up this relationship. If we call User.first.following_users, then Ruby will look for all rows in the following_users table where follower_id = 1.
Line 2: Here we are saying that a user has_many follower_users, we are telling Ruby to build this association with the FollowingUser class and the following_id as foreign key.
Now on the next two lines we are building the association between the same User model.
- Line 3: Here we are telling Ruby:
- Build a macro :following for the User class Build the relationship for this macro on the same class_name 'User'
To build this relationship, use as foreign_key the follower_id, which is available through the following_users association.
Line 4: Here we are doing exactly the same thing as on point three, except that we are now building the process for followers.