Detecting the origin of an ActiveRecord record's destruction
In a before|after_destroy
callback, you can use the
destroyed_by_association
method to access the association (therefore the parent record or parent record
type) that is causing this record to be destroyed. This is also a handy way to
tell the difference betwee a ‘direct’ destruction (
widget.destroy), and a
‘dependent’ destruction
has_many :widgets, dependent: :destroy`).
I had a use case for this recently with a before_destroy
callback intended to
prevent a record from being ‘orphaned’ - that is, a record that should always
have at least one of something, where destroying the record would cause it to
have none.
I implemeneted a ‘validating’ callback:
before_destroy :prevent_orphaned_record
private
def prevent_orphaned_record
return true unless parent.children.size == 1
errors.add(:base, :destroy_would_orphan_record)
throw :abort
end
The problem I have with this, is that when the parent record has an assocaition with dependent destroy, we don’t actually want this validation to be triggered, since, while we are orphaning the record, we are also about to destroy the parent. An example of an association like this would be:
has_many :children, dependent: :destroy
# or
has_one :child, dependent: :destroy
What we want to do, is guard the callback, so that if the parent record is
being destroyed, we can allow the record to be orphaned, since it’s about to be
destroyed. I tried a few different variations, but eventually found
destroyed_by_association
. This method returns the association, which allows
access to the parent record, as well as the parent record class.
With this method, the guard clause can easily be implemented:
def prevent_orphaned_record
return true if destroyed_by_association # Could also check the specific parent here, etc.
return true unless parent.children.size == 1
# etc
end