Вопрос

При уничтожении спокойного ресурса я хочу гарантировать несколько вещей, прежде чем разрешить продолжение операции уничтожения?По сути, мне нужна возможность остановить операцию уничтожения, если я замечу, что это приведет к переводу базы данных в недопустимое состояние?Для операции уничтожения не существует обратных вызовов проверки, так как же «проверить», следует ли принять операцию уничтожения?

Это было полезно?

Решение

Вы можете вызвать исключение, которое затем перехватить.Rails оборачивает удаления в транзакцию, что помогает.

Например:

class Booking < ActiveRecord::Base
  has_many   :booking_payments
  ....
  def destroy
    raise "Cannot delete booking with payments" unless booking_payments.count == 0
    # ... ok, go ahead and destroy
    super
  end
end

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

def before_destroy
  return true if booking_payments.count == 0
  errors.add :base, "Cannot delete booking with payments"
  # or errors.add_to_base in Rails 2
  false
  # Rails 5
  throw(:abort)
end

myBooking.destroy теперь вернет false, и myBooking.errors будет заполнено по возвращении.

Другие советы

просто примечание:

Для рельсов 3

class Booking < ActiveRecord::Base

before_destroy :booking_with_payments?

private

def booking_with_payments?
        errors.add(:base, "Cannot delete booking with payments") unless booking_payments.count == 0

        errors.blank? #return false, to not destroy the element, otherwise, it will delete.
end

Это то, что я сделал с Rails 5:

before_destroy do
  cannot_delete_with_qrcodes
  throw(:abort) if errors.present?
end

def cannot_delete_with_qrcodes
  errors.add(:base, 'Cannot delete shop with qrcodes') if qrcodes.any?
end

Ассоциации ActiveRecord has_many и has_one допускают зависимую опцию, которая гарантирует удаление связанных строк таблицы при удалении, но обычно это делается для поддержания чистоты вашей базы данных, а не для предотвращения ее недействительности.

Вы можете обернуть действие уничтожения оператором if в контроллере:

def destroy # in controller context
  if (model.valid_destroy?)
    model.destroy # if in model context, use `super`
  end
end

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

Наличие такого метода также позволит вам запретить отображение опции удаления пользователю, что улучшит взаимодействие с пользователем, поскольку пользователь не сможет выполнить незаконную операцию.

В итоге я использовал код отсюда, чтобы создать переопределение 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

Это имеет дополнительное преимущество: скрыть/показать кнопку удаления в пользовательском интерфейсе становится проще.

Вы также можете использовать обратный вызов before_destroy, чтобы вызвать исключение.

У меня есть эти классы или модели

class Enterprise < AR::Base
   has_many :products
   before_destroy :enterprise_with_products?

   private

   def empresas_with_portafolios?
      self.portafolios.empty?  
   end
end

class Product < AR::Base
   belongs_to :enterprises
end

Теперь, когда вы удаляете предприятие, этот процесс проверяет, если есть продукты, связанные с предприятиями Примечание:Вы должны написать это в верхней части класса, чтобы сначала проверить его.

Используйте проверку контекста ActiveRecord в Rails 5.

class ApplicationRecord < ActiveRecord::Base
  before_destroy do
    throw :abort if invalid?(:destroy)
  end
end
class Ticket < ApplicationRecord
  validate :validate_expires_on, on: :destroy

  def validate_expires_on
    errors.add :expires_on if expires_on > Time.now
  end
end

Я надеялся, что это будет поддерживаться, поэтому открыл вопрос о рельсах, чтобы добавить его:

https://github.com/rails/rails/issues/32376

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top