我正在为一个电子商务网站构建结帐页面,并且我有一个相当长的事务,它创建了一个新的User模型和一个新的Order模型。我将这些模型的创建封装在一个事务中,这样,如果其中一个模型的验证失败,另一个模型就不会在数据库中挂起。下面是我的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
我看到的错误是:
缺少模板orders/create, application/create with {:locale=>[:en],:formats=>[:html],:handlers=>[:erb,:builder,:coffee]}
我很困惑,为什么它试图渲染模板orders/create和application/create,而不是渲染orders/new。
是否有更好的方法来强制事务失败以便发生回滚?
我认为在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
方法中返回,否则它的执行将继续到方法结束,Rails默认渲染将发生(在这种情况下,这意味着试图找到一个create.___
模板)。
如果您不喜欢开始/救援块,您可以在raise
行中添加and return
raise ActiveRecord::Rollback and return
以上答案是正确的,但需要对渲染动作进行一些修改。
这样做:-
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不会被request捕获。所以它需要在事务块之外完成。
您也可以在嵌套的ActiveRecord::Base.transaction
中使用:requires_new => true
来使用save_point
您需要引发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