I have two scaffold-generated Models, student and class. They have a many-to-many relationship implemented with has_and_belongs_to_many.
I'd like to be able to change which classes a student is in as well as which students are taking each class. That is, I want to modify a student's classes variable (adding and removing items from it) and vice versa.
How do I do this RESTfully?
If I'm removing a class from a student's classes list, then it seems like I want to call update on my students_controller. If this is the case, then what should I pass in as a parameter to modify the classes variable? Another collection of classes (with the proper class removed)?
My other thought is just to call some action in the students_controller (say, remove_class) and pass in the ID of the class to be removed. This seems sensical but not RESTful.
What's the best way to do this?
The key to resolving this is to correctly identify the resource you are modifying. In this case, the resource you are modifying is the relationship between the class and the student, which I will refer to as an Enrollment
.
It has become customary in Rails to use has_many :through
preferentially to has_and_belongs_to_many
. You may want to change your domain logic to fit the custom, but you can also buck the trend, if you are truly certain that no metadata needs to be stored about the relationship.
One of the key ideas for REST is that RESTful resources do not need to map to models. You should create an EnrollmentsController
and add a line to config/routes.rb:
map.resources :enrollments
Then you can create and delete your relationships like so:
class EnrollmentsController < ApplicationController
def create
@student = Student.find(params[:student_id])
@course = Course.find(params[:course_id])
@student.courses << @course
if @student.save
#do happy path stuff
else
#show errors
end
end
def destroy
@student = Student.find(params[:student_id])
@course = @student.courses.find(params[:course_id])
@student.courses.delete( @course )
end
end
And you can make buttons for those actions like so:
<%= button_to "Enroll", enrollments_path(:student_id => current_student.id, :course_id => @course.id ), :method => :post %>
<%= button_to "Withdraw", enrollment_path(1, :student_id => current_student.id, :course_id => @course.id ), :method => :delete %>
The 1
on the line above acts as a placeholder where the :enrollment_id
should go and is a tiny bit of syntactic vinegar to remind you that you are bucking the will of the Rails framework.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With