Is there a way to check, within a before_destroy
hook, what object (class) called destroy
?
In the following example, when a patient
is destroyed, so are their appointments
(which is what I want); however I don't want to allow a physician
to be destroyed if there are any appointments
associated with that physician
.
Again, is there a way to do such a check in the before_destory
callback? If not, is there any other way to accomplish this "destruction check" based on the "direction" of the call (i.e. based on who called)?
class Physician < ActiveRecord::Base
has_many :appointments, dependent: :destroy
has_many :patients, through: :appointments
end
class Patient < ActiveRecord::Base
has_many :appointments, dependent: :destroy
has_many :physicians, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :patient
belongs_to :physician
before_destroy :ensure_not_referenced_by_anything_important
private
def ensure_not_referenced_by_anything_important
unless patients.empty?
errors.add(:base, 'This physician cannot be deleted because appointments exist.')
false
end
end
end
Note that dependent: :destroy
on a has_many :through
relationship only deletes the association and not the associated record (i.e. the join records will be deleted, but the associated records won't). So if you delete a patient
it will only delete the appointment
and not the physician
. Read the detailed explanation in the API docs.
I have pasted the relevant paragraphs below.
What gets deleted?
There is a potential pitfall here: has_and_belongs_to_many
and has_many :through
associations have records in join tables, as well as the associated records. So when we call one of these deletion methods, what exactly should be deleted?
The answer is that it is assumed that deletion on an association is about removing the link between the owner and the associated object(s), rather than necessarily the associated objects themselves. So with has_and_belongs_to_many
and has_many :through
, the join records will be deleted, but the associated records won’t.
This makes sense if you think about it: if you were to call post.tags.delete(Tag.find_by_name('food'))
you would want the food
tag to be unlinked from the post
, rather than for the tag itself to be removed from the database.