如何在rails中对destroy进行“验证”
-
02-07-2019 - |
题
在破坏一个宁静的资源时,我想在允许销毁操作继续之前保证一些事情?基本上,如果我注意到这样做会将数据库置于无效状态,我希望能够停止销毁操作吗?在销毁操作上没有验证回调,那么如何“验证”是否应接受销毁行动?
解决方案
您可以引发一个异常然后捕获的异常。 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
。
其他提示
只是一张纸条:
对于rails 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
其中 valid_destroy?是模型类上的一个方法,如果满足销毁记录的条件,则返回true。
使用这样的方法还可以阻止向用户显示删除选项 - 这将改善用户体验,因为用户将无法执行非法操作。
我最终使用此处的代码在activerecord上创建can_destroy覆盖: 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上的删除按钮
您还可以使用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
现在,当您删除企业时,此流程会验证是否存在与企业关联的产品 注意:您必须在类的顶部写这个,以便首先验证它。
在Rails 5中使用ActiveRecord上下文验证。
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
我希望这会得到支持,所以我打开了一个rails问题来添加它: