33

子供がいる場合にレコードが破棄されないようにしています。

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

このコードをテストすると、 before_destroy :check_for_payments により、Quotation レコードが削除されなくなります。

ただし、Submission before_destroy コールバックの :check_for_payments は、Submission の削除を停止しません。

支払いのある提出物が破棄されないようにするにはどうすればよいですか?

4

6 に答える 6

33

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

上記と同じモデルで新しいアプリを作成し、提出テストを行いました。それはかなり醜いです、私はただ学んでいます...

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

合格!お役に立てば幸いです。

于 2013-10-05T19:53:35.883 に答える
28

私が持っている場所で以下のコードを試してみます:

  1. 支払いに has_many :through アソシエーションを使用
  2. any?定義されている場合はアソシエーション カウンター キャッシュを使用し、必要に応じて SQL COUNT が失敗する場合はアソシエーションのサイズを使用することになります。
  3. 引用の列挙を避ける
  4. q.paymenthas_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
于 2013-10-02T12:15:28.013 に答える
1

false を返さないことを確認quoted?してください。has_payments?

デバッグのためにこれを試してください

def check_for_payments
    raise "Debugging #{quoted?} #{has_payments?}" # Make sure both are true
    if quoted? && has_payments?
      errors[:base] << "cannot delete submission that has already been paid"
      false
    end
  end
于 2013-10-05T12:14:52.470 に答える