如何“validation”在轨道上销毁
在破坏一个平静的资源之前,我想保证一些东西,然后我允许一个破坏操作继续? 基本上,我希望能够停止销毁操作,如果我注意到这样做会使数据库处于无效状态? 在销毁操作中没有validationcallback,那么如何“validation”是否应该接受销毁操作呢?
你可以提出一个例外,然后你会发现。 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_destroycallback。 这个callback通常用来销毁从属logging,但是你可以抛出一个exception或者添加一个错误。
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
将在返回时填充。
只是一个说明:
用于导轨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”语句中包装destroy操作:
def destroy # in controller context if (model.valid_destroy?) model.destroy # if in model context, use `super` end end
where valid_destroy? 是模型类上的方法,如果满足销毁logging的条件,则返回true。
有了这样的方法也可以让你防止显示的删除选项的用户 – 这将改善用户体验,因为用户将无法执行非法操作。
我最终使用这里的代码来创buildactiverecord上的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上隐藏/显示删除button
您也可以使用before_destroycallback来引发exception。
我有这些类或模型
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
现在,当您删除企业时,此stream程将validation是否存在与企业关联的产品。注意:您必须先将其写入类的顶部,以便首先对其进行validation。