問題描述
我如何在 Rails 中“驗證”銷毀 (How do I 'validate' on destroy in rails)
在銷毀 RESTful 資源時,我想在允許銷毀操作繼續之前保證幾件事?基本上,如果我注意到這樣做會使數據庫處於無效狀態,我希望能夠停止銷毀操作?銷毀操作沒有驗證回調,那麼如何“驗證”是否應該接受銷毀操作?
參考解法
方法 1:
You can raise an exception which you then catch. Rails wraps deletes in a transaction, which helps matters.
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.
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
will now return false, and myBooking.errors
will be populated on return.
方法 2:
just a note:
For 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
方法 3:
It is what I did with 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
方法 4:
State of affairs as of 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: :destroy
doesn't work: 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
prepend: true
is required so that dependent: :destroy
doesn't run before the validations are executed: 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_many
relation 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 :entities, dependent: :restrict_with_error
方法 5:
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.
(by Stephen Cagle、Airsource Ltd、workdreamer、Raphael Monteiro、thisismydesign、go minimal)