Rails has_many :through explained
Keeping my promise, I will try to explain has_many :through with my naive understanding.
A typical use of has_many through is a many to many relation. Let’s use a basic example between students, enrollment, and courses. A student would have many courses. A course would also have many students. A join table is the go to solution for this type of situation.
enrollments is the joint table that will house two foreign keys, student_id and course_id. The SQL query to look for all enrolled courses of a student would probably look like this
SELECT * FROM courses JOIN enrollments ON courses.id = course_id JOIN students ON student_id = students.id
In ActiveRecord model level, we will have these codes.
class Student < ActiveRecord::Base has_many( :enrollments, class_name: ‘Enrollment’, foreign_key: :student_id )
has_many( :enrolled_courses, through: :enrollments, source: :course ) end
class Enrollment < ActiveRecord::Base belongs_to( :student, class_name: ‘Student’, foreign_key: :student_id )
belongs_to( :course, class_name: ‘Course’, foreign_key: :course_id ) end
class Course < ActiveRecord::Base has_many( :enrollments, class_name: ‘Enrollment’, foreign_key: :course_id )
has_many( :enrolled_students, through: :enrollments, source: :student ) end
For the convenience of explanation and to prevent Active Record getting lost (which it actually won’t in such a simple case), I am writing everything down instead of relying on rails magic here.
Let’s focus on the two has_many through that I bolded above. If you have trouble understanding the other has_many and belongs_to, refer to my previous post. I have specified how AR should look for the courses from student and vice versa.
What would happen here is, AR will look at the :through, and following its instruction and go to the Enrollment model. Then look for the source that I specified in the belongs_to method call within the Enrollment model. And it will look for the :source that it belongs_to. Refer to the chart below. Then the course will follow the specified class_name, using the foreign_key, and go look for the courses table by stepping through the Course model.
For course looking for all the enrolled students is just the other way around. With this setup, I will be able to do some_student.enrolled_courses, and some_course.enrolled_students.
Indeed, with a very minor adjustment, I can add TAs to each course without the need of creating a new table. TAs are just students at the end of the day. Not going to further discuss how to realize that. Ask me if you have any question.














