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

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.

Hi Eric, that’s really interesting! I’ll give it a whirl and report back
:slight_smile:
I might do a documentation pull request to clarify this too.

Have a great day!

Brendon

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.
has_many_through_association

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

Haha! very good :slight_smile:

Just reporting back, it works as expected on my end too. Thanks for the
feedback. This solution feels more proper :slight_smile:

Linking in the PR for the documentation change for others finding this
thread in the future: Expanding the documentation for collection=objects [ci-skip] by erickrause · Pull Request #22644 · rails/rails · GitHub

Cheers,

Brendon

I just did that this morning.

On Thursday, December 17, 2015, spike22 [email protected]
wrote:

covered everywhere on the internet.

user.partner_ids = [1,2,3]

class School < ActiveRecord::Base
There’s a validation to ‘ensure’ uniqueness on the join tables records
[email protected]

<javascript:_e(%7B%7D,‘cvml’,‘[email protected]’);>

.
To post to this group, send email to [email protected]
<javascript:_e(%7B%7D,‘cvml’,‘[email protected]’);>.
To view this discussion on the web visit

https://groups.google.com/d/msgid/rubyonrails-talk/9eb239ab-b444-4b74-837d-503b589516a2%40googlegroups.com

https://groups.google.com/d/msgid/rubyonrails-talk/9eb239ab-b444-4b74-837d-503b589516a2%40googlegroups.com?utm_medium=email&utm_source=footer

.
For more options, visit https://groups.google.com/d/optout.

Eric Krause