【问题标题】:Render failing to render correct template in rescue_from ActiveRecord::Rollback method渲染无法在rescue_from ActiveRecord::Rollback 方法中渲染正确的模板
【发布时间】:2013-01-15 01:39:50
【问题描述】:

我正在为电子商务网站构建结帐页面,我有一个相当长的事务,它创建了一个新的用户模型和一个新的订单模型。我将这些模型的创建包装在一个事务中,这样如果一个验证失败,另一个就不会在数据库中徘徊。这是我的 OrdersController 中的精简代码:

rescue_from ActiveRecord::Rollback, with: :render_new

def render_new
  render action: 'new'
end

ActiveRecord::Base.transaction do
  @user = User.new params[:user]
  unless @user.save
    raise ActiveRecord::Rollback
  end
  //More stuff
  ...
  @order = Order.new params[:order]
  ...
  unless @order.save
    raise ActiveRecord::Rollback
  end
end

我看到的错误是这样的:

缺少模板订单/创建,使用 {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]} 的应用程序/创建}

我很困惑为什么它试图呈现模板订单/创建和应用程序/创建而不是呈现订单/新。

有没有更好的方法来强制事务失败以便回滚?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 exception-handling transactions rails-activerecord


    【解决方案1】:

    我认为将事务包装在 begin/rescue 块中时的意图更加清晰。

    def create
      begin 
        ActiveRecord::Base.transaction do
          @user = User.new params[:user]
          unless @user.save
            raise ActiveRecord::Rollback
          end
          //More stuff
          ...
          @order = Order.new params[:order]
          ...
          unless @order.save
            raise ActiveRecord::Rollback
          end
        end
      rescue ActiveRecord::Rollback
        render action: "new" and return
      end
    end
    

    你需要在create方法中return,否则它会一直执行到方法结束,Rails默认渲染会发生(在这种情况下意味着试图找到create.___ 模板).

    如果您不喜欢开始/救援块,您可以在 raise 行中添加 and return

    raise ActiveRecord::Rollback and return
    

    【讨论】:

    • 感谢您的快速回复。不幸的是,我仍然没有任何成功。我按照您的建议在我的事务周围引入了一个开始/救援块,但由于某种原因,救援块永远不会被执行。我什至切换到救援块的更通用异常 ActiveRecordError,但该块仍然没有被执行。你能想到会发生这种情况的任何原因吗,尤其是当我明确提出 ActiveRecordError 异常时?
    【解决方案2】:

    以上答案是正确的,但渲染动作需要进行一些修改。

    这样做:-

    def create
        is_project_saved = false
        is_proposal_saved = false
    
        ActiveRecord::Base.transaction do
          is_project_saved = @project.save
          is_proposal_saved = @proposal.save
          if is_project_saved && is_proposal_saved
            # Do nothing
          else
            raise ActiveRecord::Rollback
          end
        end
    
        if is_project_saved && is_proposal_saved
          # You can add more nested conditions as per you need.
          flash[:notice] = "Proposal Created Successfully."
          redirect_to project_show_path(:job_id => @project.job_id)
        else
          render :new
        end
    end
    

    ActiveRecord::Rollback 不会被 resque 捕获。所以它需要在事务块之外完成。

    您还可以在嵌套的ActiveRecord::Base.transaction 中使用:requires_new => true 来使用save_point。

    【讨论】:

      【解决方案3】:

      您需要提高 ActiveRecord::Rollback 并根据需要管理渲染/重定向。正如@WasimKhan 所说,ActiveRecord::Rollback 不会被救援捕获。

      def create
        @user = User.new params[:user]
        ActiveRecord::Base.transaction do
          if @user.save
            @order = Order.new params[:order]
            if @order.save
              redirect_to :index
            else
              raise ActiveRecord::Rollback
            end
          else
            render :new
          end
        end
        render :new if @user.id.nil?
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-26
        • 1970-01-01
        • 2019-07-21
        • 2015-03-10
        • 2020-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多