Как я могу "проверить" на уничтожение в рельсах
при уничтожении restful ресурса я хочу гарантировать несколько вещей, прежде чем я позволю продолжить операцию уничтожения? В принципе, я хочу, чтобы возможность остановить операцию уничтожения, если я замечу, что это приведет к размещению базы данных в недопустимом состоянии? В операции уничтожения нет обратных вызовов проверки, поэтому как можно "проверить", следует ли принимать операцию уничтожения?
10 ответов:
вы можете вызвать исключение, которое вы затем поймать. 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 допускают зависимую опцию, которая гарантирует, что связанные строки таблицы будут удалены при удалении, но обычно это делается для сохранения вашей базы данных в чистоте, а не для предотвращения ее недействительности.
вы можете обернуть действие destroy в Оператор " if " в контроллере:
def destroy # in controller context if (model.valid_destroy?) model.destroy # if in model context, use `super` end end
здесь valid_destroy? - это метод в классе модели, который возвращает 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
Это имеет дополнительное преимущество, что делает его тривиальным, чтобы скрыть/показать кнопку "Удалить" на ИП
У меня есть эти классы или модели
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