The power of Self -Referential Associations in Rails (and self-joins)

While working on my Rails project, I found out about the existence of the self-referential association. It is a powerful tool to know about. It might seem confusing, but by breaking it down, it makes sense. So, let’s start!

Let’s imagine we have a User table where each user can follow other users, and have other users follow him/her as well.

Self-referential association means we create a JOIN MODEL, such as Friendship, for example, which links another model, such as User to itself, so a user can have many friends (which are other users), and a friend can be befriended by a user ( a follower and a followed).

For example, using our models above, a User has many followers (which are other Users), but can also follow other people. The people a User follows are known as followed.

See the example below:

class User < ApplicationRecord

has_many :followers, foreign_key: :follower_id , class_name: "Friendship"

has_many :followed, through: :followers

has_many :followed, foreign_key: :followed_id, class_name: "Friendship"

has_many :followers, through: :followed

end

Here we have the model User which has_many relationships with followers and followed. In other words, these Users need to be connected through foreign keys that we obtain from the JOIN MODEL (Friendship — see model below).

class Friendship < ApplicationRecord

belongs_to :follower, class_name: "User"

belongs_to :followed, class_name: "User"

end

This JOIN MODEL Friendship is necessary for self-referencing the User model, by first stating that the JOIN MODEL belongs_to a follower/followed, and that a follower/followed is a type of User, which is why we specify the class_name .

What we basically do is create multiple relationships between two models. Like in the example above — a relationship is created between User and Friendship through the followers/followed.

What is Self Join?

Sometimes we will find a model that should have a relationship to itself.

The self-join has no join model! However, a foreign key is added to the same model. For example, you may want to keep track of employees in a business , which can be seen as a single database model. Within the business, you want to be able to trace relationships between employees, such as those between managers and subordinates. Each employee can be a manager or a subordinate, but they are both still employees of the business at the end of the day. The example below shows how even though Employees are a single model, we can separate different kinds of employees and establish relationships between them using a self-referencing table.

Example:

class Employee < ApplicationRecord

has_many :subordinates, class_name: "Employee", foreign_key: "manager_id"

belongs_to :manager, class_name: "Employee"

end

The example above is saying that an Employee can be a subordinate or a manager. The manager has_many subordinates, but there’s only one manager per subordinate, meaning that a particular subordinate belongs to a particular manager.

A manager (who is also an employee) can have many subordinates (other employees). To group these employees that they belong to a particular manager we foreign_key them to the manager_id.

If there was no foreign key to manager_id, then to find all subordinates of a particular manager we would have to fully go through the table. By keying employees to a manager_id we can quickly query a manager for all their employees.

As a conclusion:

Rails self referential and self joins are unique because they let us accomplish a lot more queries and structure without creating extra unneeded models like in other programming languages. I highly recommend you to learn more about them and to use them when possible.

Sources

https://guides.rubyonrails.org/association_basics.html#self-joins

Software Engineer. Flatiron School graduate

Software Engineer. Flatiron School graduate