我有一个带有 after_create 回调的模型。此回调导致在另一个模型中创建新记录。但是,如果在子记录创建过程中验证失败,则仍会保存原始事务。
这似乎不对。根据 Rails 文档,整个事情都包含在一个事务中。难道我做错了什么?
class ServiceProvision < ActiveRecord::Base
has_one :cash_receipt
after_create :receive_payment_for_service_provision, :if => Proc.new { |sp| sp.immediate_settlement == true }
private
def receive_payment_for_service_provision
cash_account = CashAccount.find_by_currency_id_and_institution_id( self.currency_id, self.institution_id )
CashReceipt.create( :account_id => account.id, :service_provision_id => self.id, :amount => self.amount, :currency_id => self.currency.id, :cash_account_id => ( cash_account ? cash_account.id : nil ) )
end
end
class CashReceipt < ActiveRecord::Base
belongs_to :service_provision
validates_presence_of :cash_account_id
end
当 CashReceipt 为 cash_account_id 传递 nil 时,它确实失败并返回错误,但是我的新 ServiceProvision 对象仍在保存。
it "should fail if a cash account doesn't exist for the currency and institution" do
currency = Factory.create( :currency )
institution = Factory.create( :institution )
service_provision = Factory.build( :service_provision, :currency_id => currency.id, :institution_id => institution.id, :immediate_settlement => true )
service_provision.save.should == false
service_provision.should have( 1 ).error
end
'ServiceProvision service provision creation should raise an error if a cash account doesn't exist for the currency and institution' FAILED expected: false,
got: true (using ==)
这似乎与文档中的这一点相矛盾
Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction depends on or you can raise exceptions in the callbacks to rollback, including after_* callbacks.
如果我手动尝试取消回调中的事务,如下所示:
cr = CashReceipt.create( :account_id => account.id, :service_provision_id => self.id, :amount => self.amount, :currency_id => self.currency.id, :cash_account_id => ( cash_account ? cash_account.id : nil ) )
unless cr.errors.empty?
errors.add_to_base("Error while creating CashReciept [#{cr.errors}].")
return false
end
然后仍然保存新的 ServiceProvision 对象。
最佳答案
移动 CacheReceipt
创建到 before_validation
筛选。由于您有 has_one
关联ServiceProvision
, CacheReceipt
对象将具有正确的 :service_provision_id
保存后。您的代码如下:
before_validation :receive_payment_for_service_provision, :if => :immediate_settlement?
def receive_payment_for_service_provision
cash_account = CashAccount.find_by_currency_id_and_institution_id( self.currency_id, self.institution_id )
self.cash_receipt.build(:account_id => account.id,
:amount => self.amount,
:currency_id => self.currency.id,
:cash_account_id => ( cash_account ? cash_account.id : nil ) )
end
现在保存在
ServiceProvision
实例将返回 false
如果在保存关联的 CacheReceipt
时出现错误.
关于ruby-on-rails - 为什么回调引用的模型中的验证不会导致我的原始事务失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2354027/