Reputation: 3938
I've been looking, and can't find a good answer for how to delete records in a HABTM table. I assume a lot of people have this same requirement.
Simply, I have Students, Classes, and Classes_Students
I want a student to be able to drop a class, or delete the HABTM record that has signed that student up for that class.
There must be a simple answer to this. Does anyone know what it is?
Upvotes: 0
Views: 3427
Reputation: 1848
The reason why .destroy or .delete does not work on this situation is due to the missing primary key in the middle table. However, our parent objects have this really cool method called {other_obj}_ids. It is a collection of ids on the left table object, of the right table object. This information is of course populated from our middle table.
So with that in mind, we have 2 object classes (Student, and Classes). Active record magic can generally figure out the middle table if you are not doing anything fancy, but it is recommended to use has_many :through.
class Student < ActiveRecord::Base
has_and_belongs_to_many :classes
end
class Classes < ActiveRecord::Base
has_and_belongs_to_many :students
end
What we can now do in terms of the middle table with this setup...
student = Student.find_by(1)
student.classes # List of class objects this student currently has.
student.class_ids # array of class object ids this student currently has
# how to remove a course from the middle table pragmatically
course = Course.find_by({:name => 'Math 101'})
# if this is actually a real course...
unless course.nil?
# check to see if the student actually has the course...
if student.class_ids.include?(course.id)
# update the list of ids in the array. This triggers a database update
student.class_ids = student.class_ids - [course.id]
end
end
I know this is a little late to answer this, but I just went through this exact situation tonight and wanted to share the solution here.
Now, if you want this deleted by the form, since you can now see how it is handled pragmatically, simply make sure the form input is nested such that it has something to the effect of:
Upvotes: 5
Reputation: 3938
So the proper answer here is to do something like this in your view:
<%= link_to 'Remove', cycle_cycles_group_path(@cycle, cycle), method: :delete %><br />
cycle
is from a block the above code is within.
@cycle
is an instance variable from the join models controller.
cycle_cycles_group_path
is the nested join table "cycles_groups" under the model "Cycle" in the routes.rb
file:
resources :cycles do
resources :cycles_groups do
end
end
and the join model controller looks like this:
def destroy
@cycles_group = CyclesGroup.find(params[:id])
@cycle = @cycles_group.cycle
@cycles_group.destroy
puts "cycle: #{@cycle}"
respond_to do |format|
format.html {redirect_to cycle_path(@cycle), notice: 'Training Week was successfully removed!'}
end
end
Upvotes: 0
Reputation: 3282
Let's say a class had a course title. You can do:
student.classes.find_by_course_title("Science").delete
Upvotes: 0
Reputation: 127
What kind of trouble are you having? Do you have the appropriate :dependent=>:destroy and :inverse_of=>[foo] on your relations?
Upvotes: 0