Подтвердить до уничтожения

У меня есть три класса: School, Account и Administratorship.

Школа

has_many :administatorships
has_many :administrators, :through => :administratorships

учетная запись

has_many :administratorships

Administratorship

belongs_to :account
belongs_to :school

before_destroy :confirm_presence_of_alternate_administratorship_in_school

protected

def confirm_presence_of_alternate_administratorship_in_school
    unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0
        errors.add_to_base "The school must have at least one administrator"
    end
end

Теперь я хотел бы, чтобы я вызвал команду destroy для экземпляра Administratorship, чтобы он добавил ошибку в модель и предотвратил разрушение модели. Я удалил оператор " unless чтобы проверить, не мешает ли это добавить ошибку, но это не так. Кажется, что наличие ошибок в модели не предотвращает разрушение.

Итак, мой вопрос, есть ли способ предотвратить разрушение с помощью проверок? Я понимаю, что мог бы определить метод, который уничтожает, только если вышеуказанное условие выполнено, но кажется, что подход проверки является более элегантным решением.

Ответ 1

Если вы вернете false из этого метода before_destroy, это предотвратит уничтожение.

Ответ 2

Это ответ Rails 5, если вы вернете false, это даст предупреждение об отказе: "Возврат false в обратных вызовах Active Record и Active Model не будет неявно останавливать цепочку обратного вызова в Rails 5.1".

def confirm_presence_of_alternate_administratorship_in_school
  return if school.administrators.count(["administratorships.account_id != #{id}"]) > 0
  errors[:base] << 'The school must have at least one administrator'
  throw :abort
end

Ответ 3

Возврат false из вашего метода проверки предотвратит уничтожение записи.

Пример:

def confirm_presence_of_alternate_administratorship_in_school
  unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0
    # errors.add_to_base() is deprecated in Rails 3. Instead do...
    errors.add(:base, "The school must have at least one administrator")

    # this will prevent the object from getting destroyed
    return false
  end
end

Боковое примечание: У меня возникли проблемы с отображением этого сообщения об ошибке. Валидация будет работать, и объект не будет удален, но не было бы сообщения, позволяющего мне узнать, что произошло. Причиной этого было то, что контроллер перенаправлялся на индексный указатель вместо рендеринга представления удаления (если есть ошибка при создании нового пользователя, например, он будет выглядеть так: action = > 'new'. В этом случае есть no delete view). Когда это произошло, переменная экземпляра, на которой было установлено сообщение об ошибке (в error.add(: base, "message" )), на самом деле является reset, что уничтожает ошибку в этом процессе.

Ответ 4

В итоге я использовал код для создания переопределения can_destroy на activerecord: https://gist.github.com/andhapp/1761098

class ActiveRecord::Base
  def can_destroy?
    self.class.reflect_on_all_associations.all? do |assoc|
      assoc.options[:dependent] != :restrict || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?)
    end
  end
end

Это имеет дополнительное преимущество: сделать тривиальным, чтобы скрыть/показать кнопку удаления на ui