Ste*_*gle 76 ruby ruby-on-rails callback
在破坏一个宁静的资源时,我想在我允许销毁操作继续之前保证一些东西?基本上,如果我注意到这样做会将数据库置于无效状态,我希望能够停止销毁操作吗?在销毁操作上没有验证回调,那么如何"验证"是否应该接受销毁操作?
Air*_*Ltd 65
您可以引发一个异常然后捕获的异常.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
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用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
Run Code Online (Sandbox Code Playgroud)
myBooking.destroy
现在将返回false,并将myBooking.errors
在返回时填充.
wor*_*mer 48
只是一个说明:
对于铁轨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
Run Code Online (Sandbox Code Playgroud)
Rap*_*iro 18
这是我用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
Run Code Online (Sandbox Code Playgroud)
thi*_*ign 11
Rails 6 的情况:
这有效:
before_destroy :ensure_something, prepend: true do
throw(:abort) if errors.present?
end
private
def ensure_something
errors.add(:field, "This isn't a good idea..") if something_bad
end
Run Code Online (Sandbox Code Playgroud)
validate :validate_test, on: :destroy
不起作用:https : //github.com/rails/rails/issues/32376
由于throw(:abort)
需要Rails 5才能取消执行:https : //makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain
prepend: true
是必需的,以便dependent: :destroy
在执行验证之前不会运行:https : //github.com/rails/rails/issues/3458
您可以从其他答案和评论中将其汇总,但我发现它们都不完整。
作为旁注,许多人使用has_many
关系作为示例,他们希望确保不会删除任何会创建孤立记录的记录。这可以更容易地解决:
has_many :entities, dependent: :restrict_with_error
您可以将destroy操作包装在控制器的"if"语句中:
def destroy # in controller context
if (model.valid_destroy?)
model.destroy # if in model context, use `super`
end
end
Run Code Online (Sandbox Code Playgroud)
哪里有valid_destroy?是模型类上的一种方法,如果满足销毁记录的条件,则返回true.
使用这样的方法还可以阻止向用户显示删除选项 - 这将改善用户体验,因为用户将无法执行非法操作.