问题
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