ruby-on-rails 如何在活动记录事务回滚时清除已修改的关联?

yxyvkwin  于 2023-03-09  发布在  Ruby
关注(0)|答案(1)|浏览(129)

今天我在rails中遇到了一个bug,这对我来说似乎有点不直观。基本上,在回滚一个事务之后,我在一些关联中仍然有未持久化的模型。我会假设rails在事务回滚时清除这些模型。
基本上我的情况如下:

class Parent < ApplicationRecord
  has_many :childs
end

class Child < ApplicationRecord
  belongs_to: parent
end

parent = Parent.first
ActiveRecord::Base.transaction do
  parent.childs.create!({ :title => 'New' })
  raise ActiveRecord::Rollback
end

pp parent.childs

#Here I was expecting parent.childs to be []

#But instead I get [#<Child id: nil>]

我假设create!是www.example.com的 Package 器Class.new。回滚操作会撤消保存操作,但不会撤消新建操作。如果我调用parent.touch或www.example.com,这会引发一个异常parent.save!
我只是想确认我的理解是正确的,在回滚时,我们必须手动清除在事务中编辑的任何关联?
是否有简单的方法来实现这一点,或者我们必须显式地编辑任何修改过的关联,如下所示:

parent.childs = parent.childs.select{|c| c.persisted? }

此外,在我们必须手动清理的情况下,是否有任何方法可以捕获回滚并在救援块中执行清理,或者我们是否必须将事务 Package 在方法中?

vybvopom

vybvopom1#

回滚仅通过打开SQL事务并在块的末尾调用COMMIT或在块中引发任何异常时调用ROLLBACK来还原对数据库所做的更改。
您正在引发ActiveRecord::Rollback,这是在回滚后不会在事务方法之外重新引发的唯一异常。如果要挽救此异常,必须在它到达事务逻辑之前挽救它,然后重新引发它。

parent = Parent.first
ActiveRecord::Base.transaction do
  parent.childs.create!({ :title => 'New' })
  raise ActiveRecord::RecordInvalid
rescue StandardError => e
  # here is the last chance to catch ActiveRecord::Rollback
  # however the database changes are not yet reverted. This means
  # that while parent.reload might work, parent.childs.reload will
  # make matters worse because now the child (which will be rolled
  # back later) has an ID and it will respond true to .persisted?
  parent.reload
  raise e
end

通常回滚事件会被ActiveRecord::StatementInvalid这样的异常触发,回滚后这个异常会被重新引发,这样你就可以捕捉到它并在那里重新加载你的模型。

parent = Parent.first
begin
  ActiveRecord::Base.transaction do
    parent.childs.create!({ :title => 'New' })
    raise ActiveRecord::StatementInvalid
  end
rescue
  parent.childs.reload
end

相关问题