问题
I have the following model:
class PhoneNumber < ActiveRecord::Base
has_many :personal_phone_numbers, :dependent => :destroy
has_many :people, :through => :personal_phone_numbers
end
I want to set up an observer to run an action in a delayed_job queue, which works for the most part, but with one exception. I want the before_destroy watcher to grab the people associated with the phone number, before it is destroyed, and it is on those people that the delayed job actually works.
The problem is, when a phone number is destroyed, it destroys the :personal_phone_numbers
record first, and then triggers the observer when it attempts to destroy the phone number. At that point, it's too late.
Is there any way to observe the destroy action before dependent records are deleted?
回答1:
While this isn't ideal, you could remove the :dependent => :destroy
from the personal_phone_numbers
relationship, and delete them manually in the observer after operating on them.
However, I think that this issue might be showing you a code smell. Why are you operating on people in an observer on phone number. It sounds like that logic is better handled in the join model.
回答2:
Use alias_method to intercept the destroy call?
class PhoneNumber < ActiveRecord::Base
has_many :personal_phone_numbers, :dependent => :destroy
has_many :people, :through => :personal_phone_numbers
alias_method :old_destroy, :destroy
def destroy(*args)
*do your stuff here*
old_destroy(*args)
end
end
回答3:
It sounds like your problem in a nutshell is that you want to gather and act on a collection of Person
when a PersonalPhoneNumber
is destroyed. This approach may fit the bill!
Here is an example of a custom callback to collect Person
models. Here it's an instance method so we don't have to instantiate a PersonalPhoneNumberCallbacks
object in the ActiveRecord model.
class PersonalPhoneNumberCallbacks
def self.after_destroy(personal_phone_number)
# Something like people = Person.find_by_personal_phone_number(personal_phone_number)
# Delayed Job Stuff on people
end
end
Next, add the callback do your ActiveRecord model:
class PersonalPhoneNumber < ActiveRecord::Base
after_destroy PictureFileCallbacks
end
Your after_destroy
callback will have the model passed down and you can act on its data. After the callback chain is complete, it will be destroyed.
References
- http://guides.rubyonrails.org/active_record_validations_callbacks.html#relational-callbacks
- http://guides.rubyonrails.org/association_basics.html#association-callbacks
回答4:
You can use a before_destroy callback in the model, then grab the data and do whatever operation you need to before destroy the parent. Something like this example should be what you are looking for:
class Example < ActiveRecord::Base
before_destroy :execute_random_method
private
def execute_random_method
...
end
handle_asynchronously :execute_random_method
来源:https://stackoverflow.com/questions/11654014/callback-before-destroy-and-before-associated-records-destroyed