在after_save回调中返回false和rollback

Jon*_*nas 7 activerecord ruby-on-rails

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

def after_save_callback
  if mycondition?
    raise ActiveRecord::Rollback
  end
end
Run Code Online (Sandbox Code Playgroud)

这个回调回滚事务但mymodel.save!返回true.如何让它返回false和rollback?

Pau*_*rth 15

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

raise ActiveRecord::RecordInvalid.new(self)
Run Code Online (Sandbox Code Playgroud)

而不是

raise ActiveRecord::Rollback
Run Code Online (Sandbox Code Playgroud)

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

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


tyo*_*213 6

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
Run Code Online (Sandbox Code Playgroud)

所以...我认为around_save已经出现在一个事务块上,所以你不需要添加额外的ActiveRecord :: Base.transaction do block,因为回滚不会传播

因此,如果要在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
Run Code Online (Sandbox Code Playgroud)

编辑:阅读我写的内容......现在看起来很难理解.关键是:如果你要aroud_save再次使用不再使用ActiveRecord::Base.transaction(在上一个例子中做)因为rails会将调用包装around_save为自己,ActiveRecord::Base.transaction所以当你raise ActiveRecord::Rollback只回滚最内部的事务时,你就可以结束具有外部结果和部分保存(如第一个示例中的那样,即失败).


Sau*_*nda 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
Run Code Online (Sandbox Code Playgroud)

  • 无论如何,Rails 将“Model#save”包装在事务中。您不应该在 around_save 挂钩内打开第二个事务。相反,您应该只引发`ActiveRecord::Rollback`。 (7认同)