Many-to-Many Recursive Relationship

A common question many new developers face is how to create a recursive many-to-many relationship. Such as if you have Students, and students can tutor other students; or if you have a tree-like structure (binary search tree, etc.)

Thinking in pure Object-Oriented design, one way would be to, in your Student class, create a list of references to other students. But what if you want to capture additional information–such as if the tutor received payment?

The best thing to do in this case, is to create a separate class to encapsulate this idea of tutorship.

When you create your table, you need to add three columns: the tutor, the tutored, and if the tutor received payment.

CREATE TABLE tutorship (
tutor_id INTEGER,
tutored_id INTEGER,
hasPaid BOOLEAN,
PRIMARY KEY (tutor_id, tutored_id)
);

Next, when you create your model class, you can specify the foreign keys so Ruby knows how to access the tutor and tutored students:


class Tutorship < ActiveRecord::Base
belongs_to :tutor, :class_name => "Student", :foreign_key => "tutor_id"
belongs_to :tutored, :class_name => "Student", :foreign_key => "tutored_id"
end

Belongs_to specifies the relationship between the classes. :class_name specifies that the target class is Student, and :foreign_key specifies that the tutor_id column holds the ID of the tutor student. (Same with tutored_id for the tutored student.)

The final step is to update your students.rb model and specify that it holds references to other students:


has_many :students, :foreign_key => "tutor_id", :class_name => "Student"
has_many :students, :foreign_key => "tutored_id", :class_name => "Student"

Here, we specify that each student has many references to other students, through the two columns tutor_id and tutored_id for the student IDs.

The last step is to modify your view for creating a new tutorship so that it shows you drop-downs containing the two student’s names. (Creating a link in the student view to add a new tutorship is more complicated and covered in a different tutorial.)

First, modify your Tutorship controller so that it grabs all the students:


def new
@tutorship = Tutorship.new
@students = Student.find_all
end

Next–and this is the trickiest bit–modify your New Tutorship view so that it shows you two drop-downs for the students, like so:


<% tutor = params["tutor"].to_i %>
From:

This code creates a drop-down. It lists all the students (by name), and if there’s a tutor already selected, then default to that option.

And that’s it! Simply paste similar code (changing tutor to tutored), and you’re good to go! Now that wasn’t so painful, was it? :)

About Ashiq Alibhai

Ashiq Alibhai, PMP, has been a Rails aficionado since 2007, and developed web applications since early 2003, where he learned PHP in one summer. As the driving-force behind RailsRocket and the Launchpad project, he seeks to share the ease of development with Rails far and wide.
This entry was posted in Development and tagged , , . Bookmark the permalink.

One Response to Many-to-Many Recursive Relationship

  1. Pingback: Dave’s Blog