关于销毁宁静的资源,我想保证一些事情之后我才能继续销毁操作?基本上,如果我注意到这样做将使数据库处于无效状态,我希望能够停止销毁操作?销毁操作上没有验证回调,因此如何“验证”是否应接受销毁操作?
Answers:
您可以提出一个异常,然后捕获。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
在返回时填充。
false
结尾before_destroy
是没有用的。从现在开始,您应该使用throw(:abort)
(@see:weblog.rubyonrails.org/2015/1/10/This-week-in-Rails/…)。
has_many :booking_payments, dependent: :restrict_with_error
只是一个注释:
护栏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
has_many :booking_payments, dependent: :restrict_with_error
这是我对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
has_many :qrcodes, dependent: :restrict_with_error
ActiveRecord关联has_many和has_one允许使用一个相关选项,以确保在删除时删除相关的表行,但这通常是为了保持数据库干净而不是防止其无效。
like_so
。
dependent
选项不允许删除实体(如果它会创建孤立的记录)(这与问题更相关)。例如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
哪里有效?是模型类上的方法,如果满足销毁记录的条件,则该方法返回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上隐藏/显示删除按钮变得微不足道的额外好处。
从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
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
您还可以使用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
on: :destroy
,请参见此问题
我希望能得到支持,所以我打开了一个rails问题来添加它: