before_destroy回调不会停止记录被删除

che*_*ell 29 callback ruby-on-rails-4

如果有孩子,我试图阻止记录被销毁.

class Submission < ActiveRecord::Base

has_many :quotations, :dependent => :destroy

 before_destroy :check_for_payments


  def quoted?
    quotations.any?
  end


  def has_payments?
   true if quotations.detect {|q| q.payment}
  end


  private

  def check_for_payments
    if quoted? && has_payments?
      errors[:base] << "cannot delete submission that has already been paid"
      false
    end
  end

end

class Quotation < ActiveRecord::Base

    #associations
    belongs_to :submission
        has_one :payment_notification   
        has_one :payment

         before_destroy :check_for_payments

private 

def check_for_payments
  if payment_notification || payment
    errors[:base] << "cannot delete quotation while payment exist"
    return false
  end
end
end
Run Code Online (Sandbox Code Playgroud)

当我测试此代码时,before_destroy:check_for_payments会阻止删除Quotation记录.

但是:提交before_destroy回调中的:check_for_payments不会停止提交被删除.

如何通过销毁付款来停止提交?

And*_*res 48

Rails 5中,throw :abort否则它将无法工作.(甚至返回false)

这样的事情应该有效:

class Something < ApplicationRecord

  before_destroy :can_destroy?

  private

  def can_destroy?
    if model.something?
      errors[:base] << "Can't be destroy because of something"
      throw :abort
  end
end
Run Code Online (Sandbox Code Playgroud)


gri*_*ocl 30

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html 订购回调(我改变了这个具体例子的措辞)

有时代码需要回调以特定顺序执行.例如,在引用被+ dependent:destroy +选项销毁之前,应该执行before_destroy回调(在这种情况下为check_for_payments).

在这种情况下,问题是当执行before_destroy回调时,引用不可用,因为首先执行destroy回调.您可以在before_destroy回调上使用prepend选项来避免这种情况.

before_destroy :check_for_payments, prepend: true
Run Code Online (Sandbox Code Playgroud)

我用上面描述的相同模型制作了一个新应用程序,然后进行了提交测试.它非常难看,我只是在学习......

class Submission < ActiveRecord::Base

  has_many :quotations, :dependent => :destroy

  before_destroy :check_for_payments, prepend: true

  def quoted?
    quotations.any?
  end

  def has_payments?
    true if quotations.detect {|q| q.payment }
  end

  private

    def check_for_payments
      if quoted? && has_payments?
        errors[:base] << "error message"
        false
      end
    end

end

class Quotation < ActiveRecord::Base

  belongs_to :submission
  has_one :payment_notification   
  has_one :payment

  before_destroy :check_for_payments

  private 

    def check_for_payments
      if payment_notification || payment
        errors[:base] << "cannot delete quotation while payment exist"
        return false
      end
    end
end

require 'test_helper'

class SubmissionTest < ActiveSupport::TestCase


  test "can't destroy" do

    sub = Submission.new
    sub.save

    quote = Quotation.new
    quote.submission_id = sub.id
    quote.save

    pay = Payment.new
    pay.quotation_id = quote.id
    pay.save

    refute sub.destroy, "destroyed record"
  end
end
Run Code Online (Sandbox Code Playgroud)

它过去了!我希望有所帮助.


And*_*ing 26

我会尝试下面的代码:

  1. 使用has_many:通过关联进行付款
  2. 通过在any?没有块的情况下使用导致使用关联计数器缓存(如果已定义)或者如果已经加载了关联的大小并且如果需要则失败SQL COUNT来避免不必要的记录检索报价和支付.
  3. 避免列举报价
  4. 避免直接测试q.payment关联代理的真实性/存在,这对has_xxx不起作用.如果您想测试在线使用情况q.payment.present?

尝试以下操作,看看你如何去:

class Submission < ActiveRecord::Base

  has_many :quotations,
    inverse_of: :submission,
    dependent: :destroy

  has_many :payments,
    through: :quotations

  before_destroy :check_for_payments, prepend: true

private

  def check_for_payments
    if payments.any?
      errors[:base] << "cannot delete submission that has already been paid"
      return false
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 这段代码不起作用.在调用before_destroy回调之前,将删除付款,因为依赖:destroy也被定义为before_destroy回调,并且因为它首先被定义,所以它将在'check_for_payments'之前被调用.我不知道为什么这个答案有这么多的赞成,当它错了? (9认同)
  • 您可以使用 `prepend: true` 在依赖销毁之前运行回调。http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html (3认同)