LoginSignup
2
1

More than 1 year has passed since last update.

複数のレコードの同時更新とエラー表示/Rails

Posted at

やまないエラーはない

 デプロイが終わったはずなのに、まだ大きなエラーがあって、いろいろわやわやしていた。アプリでは振り返りの際に複数の目標の状態を更新したりする。複数の進行中の目標について、進行中から完了に変更させたり、期限を延ばしたりできるためだ。

 複数のレコードを更新するやりかたは過去に学んだ。しかし、それがひとつでもバリデーションエラーがあったらどうなるか。今のコードではエラーが出たやつだけ保存されずに、あとは保存されてしまう。なんかいただけない。

振り返り
 振り返りレコード保存OK.

関連づけられた目標達
- 目標A
  状態変更終了。 進行中→完了
- 目標B
  締め切り変更 →2002/1/1
   バリデーションエラー:過去の日付は締め切りに設定できない

 この場合、振り返りと目標Aは保存される。しかもバリデーションエラーの画面が出ない。どうしよう。

それより先にその傘をくれよ

 トランザクションという概念を導入しよう。これはその中に入っている処理のうち、ひとつでも正常に処理されないと、他の全ての処理が未完に終わるものだ。銀行のお金のやりとりのシステムとか、そんなものに利用される。

 Railsの場合はtransactionメソッドというのが提供されている。Railsのドキュメントにはモデルのメソッドとして記述されているが、ActiveRecord::Baseとつなげてもかける。

    ActiveRecord::Base.transaction do
      # 同時に行いたい処理
    end

 私はその中で

  • ひとつのReviewレコードの新規作成・保存
  • 複数のPlanレコードの更新

 行っている。ひとつでもできなかったら、バリデーションエラーが出るようにしたい。

app/controllers/reviews_controller.rb
#略
def create
#略
# いろんなパラメータ設定
#略

# @plan トップページのバリデーションエラーを表示させるためのインスタンス
    @plan = Plan.new
    all_valid = true
    ActiveRecord::Base.transaction do
      all_valid &= @review.save
      plan_keys.each_with_index do |id, i|
        
        @plan_select = Plan.find(id)
        
        if !@plan_select.update(item[i])
          @plan.errors.merge!(@plan_select.errors)
          all_valid = false
        end
      end

      if !all_valid 
        raise ActiveRecord::Rollback
      end

    end
    if all_valid
      plan_keys.each_with_index do |id, i|    
        @plan = Plan.find(id)
        all_valid &= @review.review_items.create!(plan_id: id)
      end
    end 

    if !all_valid 
      flash.now[:alert] = "投稿に失敗しました"
        @plans = Plan.where(id: plan_keys)
        @review_item_array = Array.new(@plans.size, ReviewItem.new)
        binding.pry
        render :new
    else
      flash[:notice] = "振り返りを投稿しました"
      redirect_to action: 'index'
    end

  end

なにを行ったか。まず、 all_valid は正常に動作が終了したかどうかを示すフラッグだ。  all_valid &= A all_valid = all_valid && Aということをしている。

@plan.errors.merge!(@plan_select.errors)

 これはエラーメッセージをひとつのインスタンスに格納している。ループで回していくと、いろんなインスタンスにいろんなエラーが格納されている。このままエラーを表示させようと思うと、view画面でちょっと面倒な記述をしないといけない。調べたらちょうど良いメソッドがあったので、これを使うことにした。

if !all_valid 
   raise ActiveRecord::Rollback
end

 これはわざと例外を発生させて、エラーを起こさせている、、のだが本当にこれが正しい使い方なのか。ここらへんはもうちょっと勉強が必要。

あれが欲しい全て欲しい ただ虚しい

 
 個人開発に限らず、全てのものはエラーと隣り合わせ。現実世界はエラーが表示されないこともあるので、わりと幸せな世界なのかもしれない。もうちょっと開発していこう。

 トランザクションの処理を書く際に、伊藤さんの以下の記事を参考にした。が、自分の書いたものがこれで正解なのかよくわからない。テストも書かないといけないよなぁ。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1