【问题标题】:How to raise an ActiveRecord::Rollback exception and return a value together?如何引发 ActiveRecord::Rollback 异常并一起返回值?
【发布时间】:2010-11-06 17:19:32
【问题描述】:

我有一个使用acts_as_nested_set 分叉的模型,并且我已向模型添加了一种方法来保存模型并将节点移动到一个事务中的集合中。此方法调用验证方法以确保移动有效,该方法返回 true 或 false。如果验证失败,我希望我的 save 方法引发 ActiveRecord::Rollback 以回滚事务,但也向调用者返回 false。

我的模型如下所示:

class Category < ActiveRecord::Base
  acts_as_nested_set :dependent => :destroy, :scope => :journal

  def save_with_place_in_set(parent_id)
    Category.transaction do
      return false if !save_without_place_in_set

      if !validate_move parent_id
        raise ActiveRecord::Rollback and return false
      else
        place_in_nested_set parent_id
        return true
      end
    end
  end

  alias_method_chain :save, :place_in_set

  def validate_move(parent_id)
    # return true or false if the move is valid
    # ...
  end

  def place_in_nested_set(parent_id)
    # place the node in the correct place in the set
    # ...
  end
end

但是,当我在失败的情况下调用 save 时,事务会回滚,但函数会返回 nil

>> c = Category.new(:name => "test") 
=> #<Category id: nil, name: "test" parent_id: nil, lft: nil, rgt: nil>
>> c.save_with_place_in_set 47
=> nil
>> c.errors.full_messages
=> ["The specified parent is invalid"]

【问题讨论】:

    标签: ruby-on-rails ruby transactions rollback


    【解决方案1】:

    我知道这可能有点晚了,但是我遇到了同样的问题并且刚刚发现,在一个事务块中你可以简单地引发一个异常并拯救那个......Rails 隐式回滚整个事务。所以不需要 ActiveRecord::Rollback。

    例如:

    def create
      begin
        Model.transaction do
          # using create! will cause Exception on validation errors
          record = Model.create!({name: nil})
          check_something_afterwards(record)
          return true
        end
      rescue Exception => e
        puts e.message
        return false
      end
    end
    
    def check_something_afterwards(record)
      # just for demonstration purpose
      raise Exception, "name is missing" if record.name.nil?
    end
    

    我正在使用 Rails 3.2.15 和 Ruby 1.9.3。

    【讨论】:

    • 显然,ActiveRecord::Rollback 是唯一没有在块外传输的异常。
    【解决方案2】:

    您可以将希望从函数返回的值存储在一个变量中,并将其返回到事务块之外。例如

      def save_with_place_in_set(parent_id)
        return_value = false
        Category.transaction do
          if !save_without_place_in_set
            return_value = false
          elsif !validate_move parent_id
            return_value = false
            raise ActiveRecord::Rollback
          else
            place_in_nested_set parent_id
            return_value = true
          end
        end
        return return_value
      end
    

    我最初将 return_value 设置为 false,因为您可以退出该事务块的唯一其他方法是如果其他方法之一引发 ActiveRecord::Rollback 我相信。

    【讨论】:

    • 谢谢!在 Rails 3.2.8 中仍然有效。从documentation 中我不清楚raise ActiveRecord::Rollback 在事务结束后跳转到该行。看起来它只是失败了,好像回滚根本没有中断程序流。
    【解决方案3】:

    因为处理了ActiveRecord::Rollback 异常,但ActiveRecord::Transaction 没有重新引发,我可以将我的返回移出事务块,从而在事务回滚后返回一个值。

    稍作重构:

    def save_with_place_in_set(parent_id = nil)
      Category.transaction do
        return false if !save_without_place_in_set
        raise ActiveRecord::Rollback if !validate_move parent_id
    
        place_in_nested_set parent_id
        return true
      end
    
      return false
    end
    

    【讨论】:

      猜你喜欢
      • 2014-02-10
      • 2013-03-30
      • 2011-11-11
      • 1970-01-01
      • 2012-10-16
      • 2014-05-16
      • 2018-04-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多