【Ruby on rails】createに失敗した時にrenderで戻るとurlが変わってしまう

【Ruby on rails】createに失敗した時にrenderで戻るとurlが変わってしまう

redirect_toとrenderの違いって?

まずは簡単にrenderとredirect_toの違いを見ていきます

・render: controller→view

・redirect_to: controller→URL→route→controller→view

と言う流れでそれぞれ処理します。よくcreateやupdateのアクションで成功した時にはredirect_to,失敗した時にはrenderを使用すると思います。

そこで問題となってくるのが、renderではrouteを介していないことです。そのため、createが失敗した際に、renderを使用している場合、createのurlのままviewにエラー文が表示されるという形になります。そうすると、そのページでリロードをしたら、リロードはGETが読み込まれるので、createのパスのGETのページあるいはそれがなければルーティングエラーになってしまいます。
文章で説明するよりも見ていただいた方が早いので、簡単なアプリケーションで動きを見てみます。アプリケーションには全てのカラムにpresence: trueのバリデーションを入れています。

はい。renderだとこのような挙動になってしまいますね。実際のサイトとかでバリデーションに引っかかってリロードしたら別のページに飛ぶなんていうことはまずないと思うので、変更してみます

1.renderをredirect_toに変える

まずは問題のrenderの部分をredirect_toに変えます

def create
    @item = Item.new(item_params)
    @genres = Genre.all
    if @item.save
      redirect_to admin_item_path(@item.id), notice: "商品の作成に成功しました"
    else
      redirect_to new_admin_item_path
    end
  end

2.バリデーションメッセージの取得

redirect_toに変えたことにより、エラーメッセージを取得して表示させることができなくなってしまっているので、取得できるようにします。

def create
    @item = Item.new(item_params)
    @genres = Genre.all
    if @item.save
      redirect_to admin_item_path(@item.id), notice: "商品の作成に成功しました"
    else
      redirect_to new_admin_item_path, flash: { error: @item.errors.full_messages }
    end
  end

エラーメッセージはフラッシュメッセージとして取得します

3.バリデーションの表示

定義しただけではバリデーションメッセージを表示させることはできないので、viewに表示させるための記述をしていきます。

<div class="container">
  <div class="row">
    <div class="col-sm-12 col-md-8 col-lg-6 px-5 px-sm-0 mx-auto">
      <h2 class="border-bottom">商品新規登録</h2>
      <%= form_with model: @item, url: admin_items_path, method: :post, local:true do |f| %>
        <% if flash[:error].present? %>
          <div id="error_explanation">
            <h3><%= flash[:error].count %> 件のエラーがあります</h3>
            <ul>
              <% flash[:error].each do |message| %>
                <li><%= message %></li>
              <% end %>
            </ul>
          </div>
        <% end %>
        <div class="form-group">
          <%= f.label "商品画像" %>
          <%= f.file_field :image, class: "form-control-file item_image", accept: 'image/*' %>
        </div>
        <div class="form-group">
          <%= f.label "商品名" %>
          <%= f.text_field :name, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label "説明文" %>
          <%= f.text_field :introduction, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label "ジャンル名" %>
          <%= f.select :genre_id, @genres.map{ |genre| [genre.name, @item.genre_id = genre.id]}, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label "税抜価格" %>
          <%= f.text_field :price, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label "販売ステータス" %>
          <div>
            <%= f.label :is_active, "販売中" %>
            <%= f.radio_button :is_active, true, class: "form-control" %>
          </div>
          <div>
            <%= f.label :is_active, "販売停止中" %>
            <%= f.radio_button :is_active, false, class: "form-control" %>
          </div>
        </div>
        <div class="form-group">
          <%= f.submit "商品登録", class: 'btn btn-success' %>
        </div>
      <% end %>
    </div>
  </div>
</div>

これでredirect_toでエラーメッセージを取得し、表示させることができるようになりました!ではみてみます。

しっかりとバリデーションメッセージを出した後にリロードするとその画面に戻っています。

まとめ

今回はcreateの失敗の際にrenderではなく、redirect_toで戻る方法をやってみました。urlが変わってしまうと、最悪routingエラーが出てしまうので、renderを使用する際にはそこにも注意が必要です。では今回はこの辺で!また別の記事でお会いしましょう!

カテゴリー

naska