Reputation: 3882
I have a Task model associated to a Project model via has_many through and need to manipulate the data before delete/insert via the association.
Since "Automatic deletion of join models is direct, no destroy callbacks are triggered." i can not use callbacks for this.
In Task i need all project_ids to calculate a value for Project after Task is saved. How can i disable delete or change delete to destroy on has_many through association? What is best practise for this problem?
class Task
has_many :project_tasks
has_many :projects, :through => :project_tasks
class ProjectTask
belongs_to :project
belongs_to :task
class Project
has_many :project_tasks
has_many :tasks, :through => :project_tasks
Upvotes: 29
Views: 18289
Reputation: 11
It seems like adding dependent: :destroy
to has_many :through
relation will destroy the join model (instead of delete). This is because CollectionAssociation#delete internally refers to :dependent
option to determine whether the passed records should be deleted or destroyed.
So in your case,
class Task
has_many :project_tasks
has_many :projects, :through => :project_tasks, :dependent => :destroy
end
should work.
Upvotes: 1
Reputation: 1253
Updating joins model associations, Rails add and remove records on the collection. To remove the records, Rails use the delete
method and this one will not call any destroy callback.
You can force Rails to call destroy
instead delete
when is removing records.
To do that, install the gem replace_with_destroy and pass the option replace_with_destroy: true
to the has_many association.
class Task
has_many :project_tasks
has_many :projects, :through => :project_tasks,
replace_with_destroy: true
...
end
class ProjectTask
belongs_to :project
belongs_to :task
# any destroy callback in this model will be executed
#...
end
class Project
...
end
With this, you ensure Rails invoke all the destroy callbacks. This could be very useful if you are using paranoia.
Upvotes: 1
Reputation: 3882
Seems like i have to use associations callbacks before_add
, after_add
, before_remove
or after_remove
class Task
has_many :project_tasks
has_many :projects, :through => :project_tasks,
:before_remove => :my_before_remove,
:after_remove => :my_after_remove
protected
def my_before_remove(obj)
...
end
def my_after_remove(obj)
...
end
end
Upvotes: 64
Reputation: 341
This is what I did
in the model:
class Body < ActiveRecord::Base
has_many :hands, dependent: destroy
has_many :fingers, through: :hands, after_remove: :touch_self
end
in my Lib folder:
module ActiveRecord
class Base
private
def touch_self(obj)
obj.touch && self.touch
end
end
end
Upvotes: 2