Self-referential association — rails

Saman Batool
3 min readOct 4, 2022

If you have users and are trying to implement functionality where users can track other users, creating a self-referential association with friendships can be the way to go. This will basically help you to build “friendships” between two users. To explore the association let's first talk about what the friendship model will contain. When you create a many-to-many association, a third table is used to track the ids between two different classes. For a self-referential association, you can think of the third table as ‘Friendships’, which will track the IDs of two users (yes, both from the same users table). You will have two columns: user_id and then a friend_id. Now you may be asking, where this friend_id will come from because we don’t have a separate friends class. This friend_id is actually going to be referencing the same user class.

So although the table will say user_id and friend_id, both will be ids from the users table. It works as a many-to-many relationship because multiple users can follow one user and vice versa (similar to how followers work on Instagram), however, because it is referring to the same class — we can categorize it as a self-referential association.

Now you may ask, how can we call the column a friend_id when it is referring to a user_id. We can do this by simply specifying that while it is a friend_id and we’re naming it as a friend, the class is going to be User. Rails provides a way of implementing this starting with building the friendships model:

  1. run
    rails generate model Friendship user:references
    we already know that we’re going to track the user_id with this, hence the user:references. This will create your migration file and a friendship model.
  2. Take a look at the migration file where you already have:
    t.references :user, null: false, foreign_key: true
    Let’s create references for friend by adding:
    t.references :friend, references: :users, foreign_key: { to_table: :users }
    this references friend, but the table is users and the foreign key is of that table. So it’s going to look at the user_id in that table but it will be called friend_id.
  3. Let’s move to the friendship model file. It already says
    belongs_to :user
    We know that it is also going to belong to friend for the second column. Let’s add this and specify that it comes from the user class using the class_name method:
    belongs_to :friends, class_name: ‘User’
  4. The user model needs to specify that it has many friendships and friends (through friendships) — standard many-to-many code
    has_many :friendships
    has_many :friends, through: :friendships

All three in conjunction (changes to the migration, friendship and user model files) make this a self-referential association. Now let’s run the migration — rails db:migrate — and test this out in the console. If you run Friendship.all, you will see that the table is there. If you look at just the Friendship class, you will see the friend_id along with the user_id! Now because of the association we have set up, we can add friends. Go ahead a grab two users (ie: User.first, User.last) and try out user.friends << user_2. This should add user_2 as a friend for user. So now if you run Friendship.all and user.friends, they should be populations with the association that was created.

--

--

Saman Batool

Software engineer navigating through problems in Rails and React. I like sharing my thinking processes, solutions and project learnings. I’m based in LI, NY.