Arel
Arel

Reputation: 3938

Deleting a record in a has_and_belongs_to_many table - Rails

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

Answers (4)

Venice
Venice

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

Arel
Arel

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

Rebitzele
Rebitzele

Reputation: 3282

Let's say a class had a course title. You can do:

student.classes.find_by_course_title("Science").delete

Upvotes: 0

Bob McCown
Bob McCown

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

Related Questions