An Active Record association that links to itself.
Rails Association — Part 1
What, Why, How & 3 Types of Associations — belongs to, has one & has one — through discussed.
Rails Association — Part 2
has_many, has_many-:through & HABTM associations discussed.
Table of Contents
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.
Let's elaborate on the definition of Self-Join with an example to better grasp the concept.
A classroom has some leaders, where each leader has a group of students to complete a project.
Here, instead of creating 2 tables of
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 as 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…
Creating a Table
First, we would create a table named
students where records of both group-leader and students will be present. We will set a couple of attributes to the table of
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 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
rails db:migrate command, now let's see how to set association.
Let's go through this step-by-step:
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 to let rails know to look for 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 does not allow to save a record without a foreign key value, so the
:optional option is set to
true to let rails allow records to be saved.
Since each record
belongs_to a leader, then a leader has many members. So we set
has_many with appropriate association name and again we let rails know in which table to look for records with
class_name option. And adding an additional option of
foreign_key so rails knows to look fora
grp_leader_id attribute instead of
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
=> [#<Student id: 2, name: "Zainab", project_title: "Climate Change", grp_leader_id: 1, created_at: "##", updated_at: "##">]
Extracting a group members’ 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 records of a leader is set to
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.