在ActiveRecord模型的after_save回调中,我需要ROLLBACK事务并返回false。
def after_save_callback
if mycondition?
raise ActiveRecord::Rollback
end
end
这个回调函数回滚了事务,但是mymodel.save!返回的是true。如何使它返回false并回滚?
在ActiveRecord模型的after_save回调中,我需要ROLLBACK事务并返回false。
def after_save_callback
if mycondition?
raise ActiveRecord::Rollback
end
end
这个回调函数回滚了事务,但是mymodel.save!返回的是true。如何使它返回false并回滚?
如果你想在after_save
回调中放弃保存,你应该
raise ActiveRecord::RecordInvalid.new(self)
而不是
raise ActiveRecord::Rollback
这不仅会回滚事务(回调总是发生在可能隐含的事务中,作为save
或create
的一部分),而且还会导致save
返回false
。
这里有一篇更详细的文章:http://tech.taskrabbit.com/blog/2013/05/23/rollback-after-save/
def around_save
ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback # this will actually ROLLBACK
yield # calls the actual save method
raise ActiveRecord::Rollback # this will cause a COMMIT!!! because it affect only this internal transaction.
# OTHER ACTIONS NOT EXECUTED BUT BEING A INTERNAL TRANSACTION, THE PARENT WILL COMMIT, because parent hasn't failed.
end
end
所以,我认为around_save已经在事务块中了,因此您不需要添加额外的ActiveRecord :: Base.transaction do块,因为回滚不会向上传播。
因此,如果您想在yield之前或之后回滚,则需要删除该内部事务。
def around_save
#ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback # this will actually ROLLBACK
yield # calls the actual save method
raise ActiveRecord::Rollback # this will actually ROLLBACK
# end
end
编辑:阅读我写的内容...现在似乎很难理解。重点是:如果你要使用around_save
,不要再用ActiveRecord::Base.transaction
再次包装它(像最后一个例子一样处理),因为Rails会将对around_save
的调用与自己的ActiveRecord::Base.transaction
包装在一起,所以当你执行raise ActiveRecord::Rollback
时,只会回滚最内部的事务,这可能导致奇怪的结果和部分保存(就像第一个例子中的那样是失败的)。我认为你不能使用 after_save
来实现这个功能,你应该考虑使用 around_save
:
def around_save
ActiveRecord::Base.transaction do
yield # calls the actual save method
raise ActiveRecord::Rollback if my_condition?
end
end
Model#save
中自动包含一个事务。因此,在around_save
钩子内部不应再开启第二个事务,而是应该抛出ActiveRecord::Rollback
。请注意保持原意并简化文句。 - BBonifield