0

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?

Arel
  • 3,888
  • 6
  • 37
  • 91
  • Your very question is answered here: http://stackoverflow.com/questions/1089892/rails-associations-how-do-i-remove-a-single-habtm-associated-item – Rebitzele Apr 23 '13 at 15:08
  • That's not the type of answer I'm looking for. It's to specific to that particular situation, and is to focused on other solutions. It's not easy to replicate in other applications. – Arel Apr 23 '13 at 15:29

4 Answers4

5

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:

Venice
  • 1,858
  • 1
  • 11
  • 10
0

What kind of trouble are you having? Do you have the appropriate :dependent=>:destroy and :inverse_of=>[foo] on your relations?

Bob McCown
  • 127
  • 1
  • 9
  • I don't want to delete students or classes, I want to delete only a classes_students record. As far as I understand, a :dependent => :destroy would be for if I wanted to delete all classes_students records when deleting a student. – Arel Apr 23 '13 at 15:09
0

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

student.classes.find_by_course_title("Science").delete
Rebitzele
  • 3,252
  • 1
  • 20
  • 21
0

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 
Arel
  • 3,888
  • 6
  • 37
  • 91