如何在rails中对destroy进行"验证"

Ste*_*gle 76 ruby ruby-on-rails callback

在破坏一个宁静的资源时,我想在我允许销毁操作继续之前保证一些东西?基本上,如果我注意到这样做会将数据库置于无效状态,我希望能够停止销毁操作吗?在销毁操作上没有验证回调,那么如何"验证"是否应该接受销毁操作?

Air*_*Ltd 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
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用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
Run Code Online (Sandbox Code Playgroud)

myBooking.destroy现在将返回false,并将myBooking.errors在返回时填充.

  • 我认为回调方法不再适用了. (24认同)
  • 使用Rails 5,`before_destroy`末尾的`false`是无用的.从现在开始你应该使用`throw(:abort)`(@ see:http://weblog.rubyonrails.org/2015/1/10/This-week-in-Rails/#halting-callback-chains-by-投掷aborthttpsgithubcomrailsrailspull17227). (21认同)
  • Rails在销毁之前不会验证,所以before_destroy需要返回false才能取消销毁.只是添加错误是没用的. (8认同)
  • 注意,现在它说"......好吧,继续销毁",你需要把"超级",所以实际上调用了原始的destroy方法. (3认同)
  • 在Rails 3中不推荐使用errors.add_to_base.相反,您应该执行errors.add(:base,"message"). (3认同)
  • 您的防御孤立记录的示例可以通过“has_many :booking_ payments, dependent: :restrict_with_error”更轻松地解决 (3认同)

wor*_*mer 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
Run Code Online (Sandbox Code Playgroud)

  • 相关票证:https://github.com/rails/rails/issues/3458 @sunkencity你可以在关联声明之前声明before_destroy以暂时避免这种情况. (4认同)
  • 这种方法的一个问题是,在*所有的booking_payments都被销毁后,before_destroy回调似乎被调用了*. (2认同)
  • 您的防御孤立记录的示例可以通过“has_many :booking_ payments, dependent: :restrict_with_error”更轻松地解决 (2认同)

Rap*_*iro 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
Run Code Online (Sandbox Code Playgroud)

  • 这是一篇很好的文章,解释了Rails 5中的这种行为:http://blog.bigbinary.com/2016/02/13/rails-5-does-not-halt-callback-chain-when-false-is-returned html的 (3认同)

thi*_*ign 11

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
Run Code Online (Sandbox Code Playgroud)

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


go *_*mal 6

ActiveRecord关联has_many和has_one允许使用依赖选项,以确保在删除时删除相关的表行,但这通常是为了保持数据库清洁而不是阻止它无效.


Tob*_*ede 5

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

def destroy # in controller context
  if (model.valid_destroy?)
    model.destroy # if in model context, use `super`
  end
end
Run Code Online (Sandbox Code Playgroud)

哪里有valid_destroy?是模型类上的一种方法,如果满足销毁记录的条件,则返回true.

使用这样的方法还可以阻止向用户显示删除选项 - 这将改善用户体验,因为用户将无法执行非法操作.

  • 无限循环,有人吗? (7认同)