Как мне «проверить» уничтожение в рельсах - PullRequest
71 голосов
/ 23 сентября 2008

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

Ответы [ 10 ]

62 голосов
/ 23 сентября 2008

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

47 голосов
/ 29 июля 2011

только примечание:

Для рельсов 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
16 голосов
/ 17 июня 2016

Это то, что я сделал с 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
5 голосов
/ 24 сентября 2008

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

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

Где valid_destroy? - это метод в вашем классе модели, который возвращает true, если выполнены условия для уничтожения записи.

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

5 голосов
/ 24 сентября 2008

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

4 голосов
/ 17 июня 2013

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

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

3 голосов
/ 11 января 2014

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

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

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

3 голосов
/ 23 сентября 2008

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

2 голосов
/ 25 декабря 2017

Использовать проверку контекста 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
1 голос
/ 29 марта 2018

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...