Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

HABTM association is being destroyed before the before_destory callbacks are executed

#6191

Here is some example code.
Two models with a unique HABTM ass. on each other. One model has a before_destroy callback.

class AudioTag < ActiveRecord::Base
  has_and_belongs_to_many :tracks, :uniq => true

  before_destroy do |r|
    logger.info "[AudioTag] Before destroy"
  end
end

class Track < ActiveRecord::Base
  has_and_belongs_to_many :audio_tags, :uniq => true
end

Now the problem: from a unit test I run this: audio_tag.destroy
As you can see from the following logging, the association is destroyed before the callbacks are called.

  SQL (0.2ms)  SELECT COUNT(*) FROM `audio_tags`
  SQL (0.1ms)  SELECT COUNT(*) FROM `tracks`
  SQL (0.1ms)  select count(*) from audio_tags_tracks
  AudioTag Load (0.4ms)  SELECT `audio_tags`.* FROM `audio_tags` WHERE (`audio_tags`.`id` = 235380211) LIMIT 1
  Track Load (0.4ms)  SELECT * FROM `tracks` INNER JOIN `audio_tags_tracks` ON `tracks`.id = `audio_tags_tracks`.track_id WHERE (`audio_tags_tracks`.audio_tag_id = 235380211 )
  SQL (0.1ms)  SAVEPOINT active_record_1
  AREL (0.2ms)  DELETE FROM `audio_tags_tracks` WHERE `audio_tags_tracks`.`audio_tag_id` = 235380211 AND `audio_tags_tracks`.`track_id` IN (761148351)
  SQL (0.1ms)  RELEASE SAVEPOINT active_record_1
  SQL (0.1ms)  SAVEPOINT active_record_1
[AudioTag] Before destroy
  AREL (0.2ms)  DELETE FROM `audio_tags` WHERE (`audio_tags`.`id` = 235380211)
  SQL (0.1ms)  RELEASE SAVEPOINT active_record_1
  SQL (0.2ms)  SELECT COUNT(*) FROM `audio_tags`
  SQL (0.1ms)  SELECT COUNT(*) FROM `tracks`
  SQL (18.4ms)  ROLLBACK

So to be extra clear, the two lines that matter here are:

  AREL (0.2ms)  DELETE FROM audio_tags_tracks WHERE audio_tags_tracks.audio_tag_id = 235380211 AND audio_tags_tracks.track_id IN (761148351)


[AudioTag] Before destroy

The before_destroy callbacks should be called before any modifications are made to the model.

Most likely related to #4386.

Reported by Golly · December 19th, 2010 @ 08:17 AM

State: resolved
Milestone: none
Assigned to: nobody
Importance: none

Activity

  1. Ed4
    Ed4

    This looks to me like a serious issue, because it's no longer possible to use a before_destroy callback to check for the existence of associated HABTM records. In the following example, the callback never sees any associated lessons:

    class Student < ActiveRecord::Base
      has_and_belongs_to_many :lessons
      before_destroy {|s| s.lessons.empty? }    
    end
    

    This worked in Rails 2.x, and broke when I tried to upgrade to Rails 3.

    I tried defining the callback before the association to tweak their order, but it doesn't solve the problem.

    February 4th, 2011 @ 05:52 PM

  2. Ed4
    Ed4

    Here is a patch to fix this, including a new unit test. There was an existing unit test that actually tests for the wrong behavior (it forces the deletion of association records before entering the before_destroy callback), so I updated it as well.

    February 4th, 2011 @ 09:09 PM

  3. Repository
    Repository
    • State changed from new to resolved

    (from [909588d964bf27f20142a0b4d57890114a8d4a7a]) Fixing ordering of HABTM association deletion [#6191 state:resolved]

    Signed-off-by: Santiago Pastorino santiago@wyeworks.com
    https://github.com/rails/rails/commit/909588d964bf27f20142a0b4d5789...

    February 4th, 2011 @ 09:40 PM