とんちゃんといっしょ

Cloudに関する技術とか日常とかについて書いたり書かなかったり

Railsのトランザクション処理

外部参照を行ってるデータベースの更新を行う際にトランザクションで問題が起きる。

  def create
    generate_dept_list
    generate_faculty_list
    @user = User.new(params[:user])
    @student = Student.new(params[:student])
    flg = false
    User.transaction(@user, @student) do
      @user.save
      @student.user_id = @user.id
      @student.save
    end
    if flg
      UpdatePinger.ping('students,users')
      flash[:notice] = '学生の登録完了.'
      redirect_to :action => 'list'
    else
      render :action => 'new'
    end
  end

このように書いていると、ユーザーのDBが更新されたあと学生のDBが更新されなくてもユーザーDBの登録が行われたままになった。


そこでいろいろ調べた結果

  def create
    generate_dept_list
    generate_faculty_list
    @user = User.new(params[:user])
    @student = Student.new(params[:student])
    User.transaction(@user, @student) do
      @user.save!
      @student.user_id = @user.id
      @student.save!
    end
    UpdatePinger.ping('students,users')
    flash[:notice] = '学生の登録完了.'
    redirect_to :action => 'list'
  rescue
    render :action => 'new'
  end

こう書けばトランザクションがうまくいくことが判明。
しかし、トランザクション処理が行われてもValidateが行われないので、さらに調べると

  def create
    generate_dept_list
    generate_faculty_list
    @user = User.new(params[:user])
    @student = Student.new(params[:student])
    # データ検証
    is_valid = true
    is_valid = false unless @user.valid?
    is_valid = false unless @student.valid?
    unless is_valid
      render :action => 'new'
      return
    end
    User.transaction(@user, @student) do
      @user.save!
      @student.user_id = @user.id
      @student.save!
    end
    UpdatePinger.ping('students,users')
    flash[:notice] = '学生の登録完了.'
    redirect_to :action => 'list'
  rescue
    render :action => 'new'
  end

これでトランザクション処理もValidateも行われるようになった。