【问题标题】:How can I rollback a rails save/transaction?如何回滚 Rails 保存/事务?
【发布时间】:2015-04-23 07:59:56
【问题描述】:

在我的控制器中,我有一些类似...的代码

...
if user.save
    something = Something.where("thing = ?", thing)
    if !(something.nil?)
        render json: { something: something }
    else
        #I WOULD LIKE TO ROLLBACK THE user.save HERE
    end
else
    render json: { error: user.errors.full_messages }, status: :bad_request
end

我试过了

raise ActiveRecord::Rollback, "Could not create new User, Something was not found."
render json: { error: "Could not create new User, Something was not found"}, status: :unprocessable_entity

代替上面的 ROLLBACK COMMENT 区域,但这不起作用。 user.save 最终通过。它向'rails s'吐出一些东西,但它不会回滚最后一个事务。

【问题讨论】:

  • 这个Something到底是什么?它与用户有什么关系吗?

标签: ruby-on-rails activerecord rollback


【解决方案1】:

您可以这样做:

something = Something.where(thing: thing)

if something && user.save
    render json: { something: something }
else
    render json: { error: user.errors.full_messages }, status: :bad_request
end

【讨论】:

  • 您必须执行Something.find_by 才能使something 存在或为零。
  • 其实你甚至可以把它减少到Something.exists?(thing: thing) && user.save
  • 我认为他可以将检查的那部分移到另一个方法中并使用 before_save 回调调用它。这将是一个更好的方法。
【解决方案2】:

您需要将代码包装在事务中才能使回滚正常工作。这是文档: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

类似

ActiveRecord::Base.transaction do
  # the code from your question
end

关键是user.save 调用(修改数据库)和raise ActiveRecord::Rollback 调用都需要在该块中。

【讨论】:

    【解决方案3】:

    如果您想以您提到的相同含义使用事务,您可以执行类似的操作

    User.transaction do
      if user.save
        something = Something.where("thing = ?", thing)
        if !(something.nil?)
          render json: { something: something }
        else
          raise ActiveRecord::Rollback
        end
      else
       render json: { error: user.errors.full_messages }, status: :bad_request
      end
    end
    

    不确定将响应包装在事务中是否可行,但您需要对其进行测试。

    PS: 这两行

    something = Something.where("thing = ?", thing)
    if !(something.nil?)
    

    只等价于

    if Something.exists?(thing: thing)
    

    【讨论】:

    • @AbuShady,你不觉得在另一个事务中包裹save 没有任何意义,而在内部save 是使用事务实现的!我认为我们应该让他知道什么是不好的做法,而不是让他以自己的方式做所有事情。 :)
    • 这个事务由我控制,我可以选择什么时候回滚,什么时候不回滚,当你想对一组查询进行分组并保持原子性(en.wikipedia.org/wiki/Atomicity_%28database_systems%29)时使用它条件可能是第二个条件受到第一次保存的影响,所以不能一起测试,所以如果第二个条件失败,你需要能够回滚
    • 兄弟,我很清楚这种情况,因此 Rails 正在为您提供回调。您可以使用 after_save 或 before_save 回调来执行此操作,它会按照承诺回滚并撤消所有操作。 :)
    • 这个条件很简单,或者至少没有提供太多信息,如果第二个查询与第一次保存完全无关,你的答案会更好,但如果它以某种方式链接,那么你需要做一个事务,尤其是如果第一次保存会创建更多保存。
    • 那么以一种优雅的方式做到这一点就是使用 after_save,如果我没有错,我只是在问,我总是从错误中学习。 :)
    猜你喜欢
    • 1970-01-01
    • 2013-01-17
    • 2012-07-13
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 2013-12-10
    • 2016-04-16
    相关资源
    最近更新 更多