如果有孩子,我试图阻止记录被破坏。
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会阻止删除引用记录。
然而,提交before_destroy回调中的:check_for_payments并不会阻止提交被删除。
如何通过销毁付款来停止提交?
答案 0 :(得分:43)
在 Rails 5 中,你必须my-slideshow
否则它将无效。 (甚至返回throw :abort
)
这样的事情应该有效:
false
答案 1 :(得分:30)
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html 订购回调(我改变了这个具体例子的措辞)
有时代码需要回调在特定的情况下执行 订购。例如,before_destroy回调(check_for_payments in 这个案例)应该在报价被销毁之前执行 + dependent:destroy +选项。
在这种情况下,问题是before_destroy回调是 执行时,引用不可用,因为destroy回调 先被执行。您可以使用前置选项 before_destroy回调以避免这种情况。
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
它过去了!我希望有所帮助。
答案 2 :(得分:23)
我会尝试下面的代码:
any?
而不使用阻止导致使用关联计数器缓存(如果已定义),或者如果已加载关联的大小而导致使用SQL COUNT(如果需要)失败,则避免对报价和付款进行不必要的记录检索。 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
答案 3 :(得分:1)
据我所知,当对与Submission
有关联的类(dependent => :destroy
)的对象调用destroy时,如果相关模型中的任何回调失败,在你的情况下Quotation
,然后Submission
类对象仍将被删除。
因此,为了防止这种行为,我们必须采用目前可以想到的方法:
1)不是在Quotation#check_for_payments
中返回false,而是可以在Submission
模型中引发异常并优雅地处理它,这将完成ROLLBACK
,并且不允许任何记录被摧毁。
2)您可以检查quotations
实例的任何Submission
是否在payment
方法本身中具有payment_notification
/ Submission#check_for_payments
,这会阻止删除Submission
对象。
答案 4 :(得分:1)
确保quoted?
和has_payments?
不会返回false。
有关调试,请尝试此
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
答案 5 :(得分:0)
在Rails 5中你也可以:
def destroy
quoted? && has_payments? ? self : super
end
submission.destroy # => submission
submission.destroyed? # => true/false