在 after_save 回调中返回 false 并回滚

10

在ActiveRecord模型的after_save回调中,我需要ROLLBACK事务并返回false。

def after_save_callback
  if mycondition?
    raise ActiveRecord::Rollback
  end
end

这个回调函数回滚了事务,但是mymodel.save!返回的是true。如何使它返回false并回滚?

3个回答

22

如果你想在after_save回调中放弃保存,你应该

raise ActiveRecord::RecordInvalid.new(self)

而不是

raise ActiveRecord::Rollback

这不仅会回滚事务(回调总是发生在可能隐含的事务中,作为savecreate的一部分),而且还会导致save返回false

这里有一篇更详细的文章:http://tech.taskrabbit.com/blog/2013/05/23/rollback-after-save/


9
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时,只会回滚最内部的事务,这可能导致奇怪的结果和部分保存(就像第一个例子中的那样是失败的)。

0

我认为你不能使用 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

7
Rails会在Model#save中自动包含一个事务。因此,在around_save钩子内部不应再开启第二个事务,而是应该抛出ActiveRecord::Rollback。请注意保持原意并简化文句。 - BBonifield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接