1

我正在为电子商务网站构建结帐页面,并且我有一个相当长的事务,它创建了一个新的用户模型和一个新的订单模型。我将这些模型的创建包装在一个事务中,这样如果一个验证失败,另一个就不会在数据库中徘徊。这是我的 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]} 的应用程序/创建

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

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

4

3 回答 3

3

我认为将事务包装在开始/救援块中时,意图更加清晰。

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,否则它会一直执行到方法的末尾,并且会发生 Rails 默认渲染(在这种情况下,它意味着试图找到一个create.___模板)

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

raise ActiveRecord::Rollback and return
于 2013-01-15T01:48:47.570 回答
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 捕获。所以它需要在事务块之外完成。

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

于 2014-04-16T08:33:20.300 回答
0

您需要提高 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
于 2016-03-01T19:35:01.117 回答