Render failing to render correct template in rescue_from ActiveRecord::Rollback method

瘦欲@ 提交于 2020-01-02 23:59:24

问题


I'm building the checkout page for an e-commerce site, and I have a fairly long transaction that creates a new User model and a new Order model. I wrapped the creation of these models in a transaction so that if validation for one fails, the other isn't hanging around in the database. Here's the trimmed-down code in my 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

The error I'm seeing is this:

Missing template orders/create, application/create with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}

I'm confused as to why its trying to render the templates orders/create and application/create instead of rendering orders/new.

Is there a better way to force the transaction to fail so that the rollback will occur?


回答1:


I think the intention is a bit clearer when wrapping the transaction in a begin/rescue block.

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

You need to return in the create method, otherwise it's execution will continue to the end of the method and Rails default render will occur (in this case it means attempting to find a create.___ template).

If you don't like the begin/rescue block you can just add an and return to the raise lines

raise ActiveRecord::Rollback and return



回答2:


Above answer is correct but some modification is required for rendering action.

Do it like this:-

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 will not be caught by resque. So it needs to be done outside transaction block.

You can also use save_point using :requires_new => true in nested ActiveRecord::Base.transaction.




回答3:


You need to raise the ActiveRecord::Rollback and manage the render/redirect as you desire. As @WasimKhan said, the ActiveRecord::Rollback will not be caught by rescue.

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


来源:https://stackoverflow.com/questions/14329877/render-failing-to-render-correct-template-in-rescue-from-activerecordrollback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!