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.
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? :)
Pingback: Dave’s Blog