Не удается отобразить правильный шаблон в методе escape_from ActiveRecord::Rollback

Я создаю страницу оформления заказа для сайта электронной коммерции, и у меня есть довольно длинная транзакция, которая создает новую модель пользователя и новую модель заказа. Я обернул создание этих моделей в транзакцию, чтобы в случае сбоя проверки одной из них другая не зависала в базе данных. Вот урезанный код в моем 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]}

Я смущен тем, почему он пытается отобразить заказы/создание шаблонов и приложение/создать вместо рендеринга заказов/новых.

Есть ли лучший способ принудительно завершить транзакцию, чтобы произошел откат?


person Jordan Brown    schedule 15.01.2013    source источник


Ответы (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.___)< /эм>.

Если вам не нравится блок begin/rescue, вы можете просто добавить and return к raise строкам.

raise ActiveRecord::Rollback and return
person deefour    schedule 15.01.2013
comment
Спасибо за быстрый ответ. К сожалению, я до сих пор не добился успеха. Я ввел блок начала/спасения вокруг своей транзакции, как вы предложили, но по какой-то причине блок восстановления никогда не выполняется. Я даже переключился на более общее исключение для спасательного блока, ActiveRecordError, но блок все еще не был выполнен. Можете ли вы придумать какие-либо причины, по которым это может произойти, особенно когда я явно вызываю исключение ActiveRecordError? - person Jordan Brown; 15.01.2013

Ответ выше правильный, но для рендеринга требуется некоторая модификация.

Сделай это так:-

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. Поэтому это нужно сделать вне блока транзакций.

Вы также можете использовать save_point, используя :requires_new => true во вложенном ActiveRecord::Base.transaction.

person RohitPorwal    schedule 16.04.2014

Вам нужно поднять 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
person monteirobrena    schedule 01.03.2016