6

Through many iterations of testing, I just noticed that my join table that represents a HABTM relationship between two models isn't removing entries when instances of these models get deleted. Do I need to do something special when removing an instance of a model that has HABTM relationships?

JJD
  • 50,076
  • 60
  • 203
  • 339
James
  • 5,273
  • 10
  • 51
  • 76

2 Answers2

7

Upon closer inspection HABTM relationships should be removing join table entries. However neither HABTM relationships or the relationship I described in the original version (see post history) of this solution will remove those join table entries when you eliminate the record with the delete method. ActiveRecord::Base#delete does not trigger any callbacks, such as the ones a HABTM relationship establishes to remove orphaned entries from the join table. Instead you should be using ActiveRecord::Base#destroy.

You will have to use raw SQL to remove the unneeded entries. If you decide to create a join model, you can iterate through entries in the join model, deleting those without an association.

Example:

class Foo < ActiveRecord::Base
  has_many :foo_bars, :dependent => :destroy
  has_many :bars, :through => :foo_bars
end

class FooBar < ActiveRecord::Base
  belongs_to :foo
  belongs_to :bar
end

class Bar < ActiveRecord::Base
  has_many :foo_bars, :dependent => :destroy
  has_many :foos, :through => :foo_bars
end

FooBar.all.each{|fb| fb.destroy? if fb.foo.nil? || fb.bar.nil? }
JJD
  • 50,076
  • 60
  • 203
  • 339
EmFi
  • 23,435
  • 3
  • 57
  • 68
  • So is there a point to "dependent => :destroy" if I have to "FooBar.all.each{|fb| fb.destroy? if fb.foo.nil? || fb.bar.nil? }" anyways? – James Dec 17 '09 at 07:28
  • does the "dependent => :destroy" even do anything? it doesn't seem to. Do I need to implement destroy or something? – James Dec 17 '09 at 07:29
  • 1
    `FooBar.all.each{ ... }` is just to get rid of the orphaned records in the join table. After moving to the has\_many :though relationship with the join model association marked as :dependent => :destroy, you will not orphan records in the join table if you delete a record they belong to. Adding :dependent => :destroy option will destroy all associated records related to one that you're destroying. However, it doesn't trigger on delete. You should read the documentation on ActiveRecord::Base#destroy, ActiveRecord::Base#delete and ActiveRecord::Associations::ClassMethods. – EmFi Dec 17 '09 at 07:55
0

The entries in the join table should be getting deleted without you doing anything special. You can add the :delete_sql option to your code to change the behavior if you have a weird situation. Deleting the object on the other side of the join is not a default behavior.

MattMcKnight
  • 8,185
  • 28
  • 35