我如何'验证'在铁轨上的破坏

问题描述 投票:75回答:11

关于销毁静态资源,在允许销毁操作继续之前,我想保证一些事情吗?基本上,如果我注意到这样做会导致数据库处于无效状态,我希望能够停止销毁操作?销毁操作没有验证回调,因此如何“验证”是否应接受销毁操作?

ruby-on-rails ruby callback
11个回答
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

或者,您可以使用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


1
投票

我希望能得到支持,所以我打开了一个rails问题来添加它:

https://github.com/rails/rails/issues/32376


0
投票

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

因为需要Rails 5 throw(:abort)才能取消执行:https://makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain

prepend: true是必需的,以便在执行验证之前不运行dependent: :destroyhttps://github.com/rails/rails/issues/3458

您可以从其他答案和评论中一起钓鱼,但我发现它们都不完整。

作为附带说明,许多人以has_many关系为例,在此情况下,如果要创建孤立记录,他们要确保不要删除任何记录。这可以很容易解决:

has_many :entities, dependent: :restrict_with_error


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

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

6
投票

ActiveRecord关联has_many和has_one允许使用一个相关选项,以确保在删除时删除相关的表行,但这通常是为了使数据库保持干净,而不是防止其无效。


5
投票

您可以将destroy操作包装在控制器的“ if”语句中:

def destroy # in controller context
  if (model.valid_destroy?)
    model.destroy # if in model context, use `super`
  end
end

其中valid_destroy?是模型类上的一种方法,如果满足销毁记录的条件,该方法将返回true。

具有这样的方法还可以防止向用户显示删除选项-由于用户将无法执行非法操作,因此可以改善用户体验。


4
投票

我最终从这里使用代码在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上隐藏/显示删除按钮的简单好处


3
投票

您还可以使用before_destroy回调引发异常。


3
投票

我有这些课程或模型

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

现在删除企业时,此过程将验证是否有与企业相关的产品注意:必须先在类的顶部编写此代码,然后才能对其进行验证。


2
投票

在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
© www.soinside.com 2019 - 2024. All rights reserved.