Search code examples
ruby-on-railsassociationsdestroy

has_many through association dependent destroy under condition of who called destroy


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

Solution

  • Just say:

    class Physician < ActiveRecord::Base
      has_many :appointments, dependent: :restrict_with_exception
      has_many :patients, through: :appointments
    end
    

    Note the dependent: :restrict_with_exception. This will cause Active Record to refuse to destroy any Physician records that have associated Appointment records.

    See the API docs and the association basics guide.