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? Hopefully there's a technical reason because otherwise it's just crazy!
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:
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
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.
So I ran into the same issue the other day. 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.
Typically if you want to delete something through has_many
association you put dependent: :destroy
there:
class User
has_many :partnerships, dependent: :destroy
has_many :partners, through: :partnerships
end
If you want to destroy partners
as well as partnerships you have to add this dependency to Partnership
s model:
class Partnership
belongs_to :partner, dependent: :destroy
belongs_to :user
end
When object is destroyed it calls destroy on every object where destroy dependency is provided. So User
calls destroy on every Partnership
, and every Partnership
calls destroy on every Partner
.
"Why it can not be used with through
" - well, the answer is "since deletion is direct". I know it does not says much (for me either), but on the other hand as for me adding dependencies to objects which are not linked directly is a bad idea.
Consider example above: if dependent - destroy will work on partners and destroy them – should it destroy join model as well? Certainly yes, because in other case it will lead to data corruption, but you may lose some data which could lay in the join model, so in some cases – no, you don't want to destroy join model. This means Rails team would have to add new parameter – delete_join
to indicate if you want to save that model or not.
And that's just a bad design, since we already have better approach – add dependencies in the join models.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With