Ruby-on-rails 如何在 Rails 中“验证”销毁
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/123078/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I 'validate' on destroy in rails
提问by Stephen Cagle
On destruction of a restful resource, I want to guarantee a few things before I allow a destroy operation to continue? Basically, I want the ability to stop the destroy operation if I note that doing so would place the database in a invalid state? There are no validation callbacks on a destroy operation, so how does one "validate" whether a destroy operation should be accepted?
在销毁一个安静的资源时,我想在允许销毁操作继续之前保证一些事情?基本上,如果我注意到这样做会使数据库处于无效状态,我希望能够停止销毁操作?销毁操作没有验证回调,那么如何“验证”销毁操作是否应该被接受?
采纳答案by Airsource Ltd
You can raise an exception which you then catch. Rails wraps deletes in a transaction, which helps matters.
您可以引发一个异常,然后捕获该异常。Rails 将删除包装在一个事务中,这有助于解决问题。
For example:
例如:
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
Alternatively you can use the before_destroy callback. This callback is normally used to destroy dependent records, but you can throw an exception or add an error instead.
或者,您可以使用 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.destroywill now return false, and myBooking.errorswill be populated on return.
myBooking.destroy现在将返回 false,并将myBooking.errors在返回时填充。
回答by workdreamer
just a note:
只是一个注意事项:
For rails 3
对于导轨 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
回答by Raphael Monteiro
It is what I did with Rails 5:
这是我对 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
回答by go minimal
The ActiveRecord associations has_many and has_one allows for a dependent option that will make sure related table rows are deleted on delete, but this is usually to keep your database clean rather than preventing it from being invalid.
ActiveRecord 关联 has_many 和 has_one 允许一个依赖选项,以确保相关的表行在删除时被删除,但这通常是为了保持你的数据库干净而不是防止它无效。
回答by Toby Hede
You can wrap the destroy action in an "if" statement in the controller:
您可以将销毁操作包装在控制器中的“if”语句中:
def destroy # in controller context
if (model.valid_destroy?)
model.destroy # if in model context, use `super`
end
end
Where valid_destroy?is a method on your model class that returns true if the conditions for destroying a record are met.
哪里valid_destroy?是模型类上的一个方法,如果满足销毁记录的条件,则返回 true。
Having a method like this will also let you prevent the display of the delete option to the user - which will improve the user experience as the user won't be able to perform an illegal operation.
拥有这样的方法还可以让您阻止向用户显示删除选项 - 这将改善用户体验,因为用户将无法执行非法操作。
回答by Hugo Forte
I ended up using code from here to create a can_destroy override on activerecord: https://gist.github.com/andhapp/1761098
我最终使用这里的代码在 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
This has the added benefit of making it trivial to hide/show a delete button on the ui
这有一个额外的好处,即在 ui 上隐藏/显示删除按钮变得微不足道
回答by Mateo Vidal
I have these classes or models
我有这些类或模型
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
Now when you delete an enterprise this process validates if there are products associated with enterprises Note: You have to write this in the top of the class in order to validate it first.
现在,当您删除企业时,此过程会验证是否存在与企业关联的产品 注意:您必须将其写在类的顶部以便首先验证它。
回答by Matthias Winkelmann
You can also use the before_destroy callback to raise an exception.
您还可以使用 before_destroy 回调来引发异常。
回答by thisismydesign
State of affairs as of Rails 6:
Rails 6 的情况:
This works:
这有效:
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
validate :validate_test, on: :destroydoesn't work: https://github.com/rails/rails/issues/32376
validate :validate_test, on: :destroy不起作用:https: //github.com/rails/rails/issues/32376
Since Rails 5 throw(:abort)is required to cancel execution: https://makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain
由于throw(:abort)需要Rails 5才能取消执行:https: //makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain
prepend: trueis required so that dependent: :destroydoesn't run before the validations are executed: https://github.com/rails/rails/issues/3458
prepend: true是必需的,以便dependent: :destroy在执行验证之前不会运行:https: //github.com/rails/rails/issues/3458
You can fish this together from other answers and comments, but I found none of them to be complete.
您可以从其他答案和评论中将其汇总,但我发现它们都不完整。
As a sidenote, many used a has_manyrelation as an example where they want to make sure not to delete any records if it would create orphaned records. This can be solved much more easily:
作为旁注,许多人使用has_many关系作为示例,他们希望确保不会删除任何会创建孤立记录的记录。这可以更容易地解决:
has_many :entities, dependent: :restrict_with_error
has_many :entities, dependent: :restrict_with_error
回答by swordray
Use ActiveRecord context validation in Rails 5.
在 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

