The Foundation

All the 6 basic associations that I covered in Part 1, Part 2 & Polymorphic had one thing in common, all associations required more than 1 or in some cases more than 2 models to be associated with each other. However, to use Self-Join association, you require only 1 model.

So what is it?

A Self-Join association helps us to associate each instances within the same table through a foreign-key which references to its own primary-key.

An Example

Let's elaborate on the definition of Self-Join with an example to better grasp the concept.

A classroom has some leaders, and each leader has a group of students to complete a project.

Here, instead of creating 2 tables of leaders and students with has_many-belongs_to association, we can set the same relation with just a single table, because even a leader is a student of a classroom just like other students, and giving 2 different tables would create duplicate entries of leaders.

So, does this mean we would need to give has_many-belongs_to association to the same table? But how? Let’s go through it step-by-step…

An overview of the example

Creating a Table

First, we would create a table named students where records of both group leaders and students will be present. We will set a couple of attributes to the table of name & project_title.

Here’s the twist, how would each record or student know who’s his/her group project leader? Simple, with the help of a foreign key. Usually, for other associations, a foreign key value is set to its opposite table but here we set the foreign key to the students table itself.

The naming of a foreign key can be set to anything of relevance to what we are assigning to that record. Here each record is a student of a classroom who has a leader, so an appropriate name would be ‘group leader’.

Now, let's create the table in the database:

rails g model Student name:string project_title:string grp_leader:references

Running migration will create the following migration file:

Line #6 has been modified to let rails know in which table to find the foreign key, grp_leader. More on this in Unconventional Foreign-key.

After migrating or creating a table in the database with the rails db:migrate , we will now take a look on setting up an association.

Association

Let's go through this step-by-step:

belongs_to:

Since we know each student or record in the students table belongs to a leader, we give an appropriate association name with the addition of class_name option to it, it will let Rails know to look for the foreign key in students table. More on this in Unconventional Foreign-key.

However, not all records will have a foreign-key value set. Those records who are leaders, who don’t belong to any other leader will have an empty value in their foreign-key attribute. Rails do not allow saving a record without a foreign key value, so the :optional option is set to true to let rails allow records to be saved.

has_many:

Since each record belongs_to a leader, then a leader has many members. So we set has_many with the appropriate association name and again we let rails know in which table to look for records with theclass_name option. And adding an additional option of foreign_key so Rails knows to look for a grp_leader_id attribute instead of a grp_members_id.

Data

After setting proper association, let's fill some records to our table through the rails console:

Creating leaders’ data:

# leader_1 = Student.create(name: "Sakina", project_title: "Climate Change")

Now, creating members for this project:

# leader_1.grp_members << Student.create(name: "Zainab", project_title: "Climate Change")

Extracting all group members of a leader

# leader_1.grp_members
=> [#<Student id: 2, name: "Zainab", project_title: "Climate Change", grp_leader_id: 1, created_at: "##", updated_at: "##">]

Extracting a group member’s leader:

# grp_member = Student.find_by(name: "Zainab")
# grp_member.grp_leader
=> <Student id: 1, name: "Sakina", project_title: "Climate Change", grp_leader_id: nil, created_at: "##", updated_at: "##">

The value of a foreign key for the records of a leader is set to nil.

Source Code

I hope I have achieved to make you understand the topic thoroughly. If you have any questions or other comments, feel free to leave them below. Also, if you found this article useful, you can share it so others can find it as well.

--

--