Why doesn't collection=objects on has_many :through trigger destroy callbacks on the join model?

500 views
Skip to first unread message

spike22

unread,
Dec 14, 2015, 7:43:57 PM12/14/15
to Ruby on Rails: Talk
I've asked this on on Stack Overflow but didn't receive much of a response:

The Rails 4 documentation says this regarding destroy callbacks on the join model for a has_many :through relationship:

collection=objects Replaces the collections content by deleting and adding objects as appropriate. If the :through option is true callbacks in the join models are triggered except destroy callbacks, since deletion is direct.

Thankfully it's documented at least, but I want to know why on earth this is the case? It makes more sense to trigger destroy callbacks (or have the option to) on a :through since these types of models can have destroy callbacks and other associations.

In my case I had a has_and_belongs_to_many relationship on the join tables model off to another model. The records on that second join table would never be deleted when the associated records on the first join table were deleted. I resorted to this which feels hacky, and I have to repeat myself on each side of the :through relationship:

class SchoolsTemplate < ActiveRecord::Base

  belongs_to :school
  belongs_to :template

  has_and_belongs_to_many :groups
end

class School < ActiveRecord::Base
  has_many :schools_templates, dependent: :destroy
  has_many :templates, through: :schools_templates, before_remove: :remove_groups_school_templates

  private

  def remove_groups_school_templates(template)
    schools_templates.where(template: template).first.groups.clear
  end
end

There's a validation to 'ensure' uniqueness on the join tables records between the two foreign keys, so that's why I can call first in the callback.

Eric Krause

unread,
Dec 17, 2015, 12:23:50 PM12/17/15
to Ruby on Rails: Talk
I responded on stackoverflow, but I'll do here as well so that it is covered everywhere on the internet.

In my case I was doing something similar to what you were doing and was running into the same issue with the join table being deleted instead of destroyed.

I started looking through the code and I believe the documentation is just out of date.

All you need to do is add the dependent: :destroy to the has_many :through relationship.

    class User
      has_many :partnerships, dependent: :destroy
      has_many :partners, through: :partnerships, dependent: :destroy
    end

The pain I was dealing with was:
    
    user.partner_ids = [1,2,3]
    #creates the relationships
    user.partner_ids = []
    #was deleting the records from partnerships without callbacks.

The dependent: :destroy on the partners relationship fixed that.  Callbacks are now being run and things are good again.

Eric

spike22

unread,
Dec 17, 2015, 4:38:20 PM12/17/15
to Ruby on Rails: Talk
Hi Eric, that's really interesting! I'll give it a whirl and report back :) I might do a documentation pull request to clarify this too.

Have a great day!

Brendon

Eric Krause

unread,
Dec 17, 2015, 4:39:49 PM12/17/15
to rubyonra...@googlegroups.com
I just did that this morning. 
--
You received this message because you are subscribed to a topic in the Google Groups "Ruby on Rails: Talk" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/rubyonrails-talk/eGJRC4hDDuQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to rubyonrails-ta...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/9eb239ab-b444-4b74-837d-503b589516a2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Eric Krause

spike22

unread,
Dec 17, 2015, 4:43:07 PM12/17/15
to Ruby on Rails: Talk
Haha! very good :)
To unsubscribe from this group and all its topics, send an email to rubyonrails-talk+unsubscribe@googlegroups.com.
To post to this group, send email to rubyonrails-talk@googlegroups.com.


--

Eric Krause

spike22

unread,
Dec 18, 2015, 4:46:37 AM12/18/15
to Ruby on Rails: Talk
Just reporting back, it works as expected on my end too. Thanks for the feedback. This solution feels more proper :)

Linking in the PR for the documentation change for others finding this thread in the future: https://github.com/rails/rails/pull/22644

Cheers,

Brendon
Reply all
Reply to author
Forward
0 new messages