LoginSignup
39
37

More than 5 years have passed since last update.

【Rails】データ保存の失敗時には、'redirect_to'ではなく'render'を利用した方が良い理由

Last updated at Posted at 2017-05-08

はじめに

データの保存に成功した場合は特定のページを表示 させて、失敗した場合は現在のページにまた戻らせたい、という条件分岐を設定することがあるかと思います。
このような時、どちらの場合にも'redirect_to'を使用することができますが、失敗した場合は'render'を使用した方が良いとされています。
なぜでしょうか?

対象読者

'redirect_to'と'render'の違いがよくわからない。全部'redirect_to'じゃだめなの?と思っている人。
rails初学者。

'redirect_to'と'render'の違い

redirect_to

リクエストを別のアクションに委譲する。
つまりクライアントにリダイレクト先URLを返却し、クライアントは再度そのURLにリクエストを上げることになる。

render

レタリングするテンプレートを指定する。
デフォルトはアクション名(コントローラのメソッド名)と同じViewファイルを使用してHTMLを作成するが、
renderで使いたいViewファイルを指定することができる。
エラーメッセージ表示のために、元の画面に差し戻す際などに利用する。

さて、この説明では、書いている私自身でもよくわかりません。
具体例を見て考えてみましょう。

具体例

下記のようなアプリのコントローラーを考えてみます。
indexにいる時、@messageをビューのform_forに渡し、ユーザーがメッセージの投稿ができる、チャットのようなアプリです。

messages_controller.rb
class MessagesController < ApplicationController

  def index
    @message = Message.new
    @messages = Message.all
  end

  def create
    @message = Message.new(message_params)
    if @message.save
      # メッセージの保存に成功した場合
    else
      # メッセージの保存に失敗した場合
    end
  end

  private
  def message_params
    params.require(:message).permit(:body).merge(user_id: current_user.id)
  end
end

保存に成功した場合も、失敗した場合も、indexに飛ばしたい場合どのようにすれば良いでしょうか?
redrect_toを使用すれば、このように書くことができますね。

'redirect_to'のみで遷移させた場合

messages_controller.rb
class MessagesController < ApplicationController

  def index
    @message = Message.new
    @messages = Message.all
  end

  def create
    @message = Message.new(message_params)
    if @message.save
      # メッセージの保存に成功した場合
      redirect_to :index, notice: 'メッセージの送信に成功しました'
    else
      # メッセージの保存に失敗した場合
      redirect_to :index, alert: 'メッセージの送信に失敗しました'
    end
  end

  private
  def message_params
    params.require(:message).permit(:body).merge(user_id: current_user.id)
  end
end

これで正常に動くはずです。
しかし、'redirect_to'では、下記のような挙動をする、と最初に書きました。

リクエストを別のアクションに委譲する。
つまりクライアントにリダイレクト先URLを返却し、クライアントは再度そのURLにリクエストを上げることになる。

これを限りなく平たく言うと、 「あるURLに新しくアクセスし直す」 という意味になるでしょうか。
この場合で言えば、'redirect_to'によってindex→indexと移動するため、 「更新ボタンを押した」 というイメージになりますね。
他のビューへリダイレクトする場合も同様です。

さて、ここで考えなければならないことが一つ出てきます。
データの保存に成功した場合は、そのデータをビューに表示させたいため、「更新ボタン」を押してくれた方がいいですよね。
でも、失敗した場合は、同じ画面を表示するだけで、わざわざ「更新ボタン」を押してもらう必要はありません。
データが保存されていない以上、ビューには新しく表示すべきものがないからです。
こんな時に、'render'が役に立ちます。
以下のように書き直しましょう。

失敗時には'render'を使用した場合

messages_controller.rb
class MessagesController < ApplicationController

  def index
    @message = Message.new
    @messages = Message.all
  end

  def create
    @message = Message.new(message_params)
    if @message.save
      # メッセージの保存に成功した場合
      redirect_to :index, notice: 'メッセージの送信に成功しました'
    else
      # メッセージの保存に失敗した場合
      flash.now[:error] = 'メッセージの送信に失敗しました'
      render :index
    end
  end

  private
  def message_params
    params.require(:message).permit(:body).merge(user_id: current_user.id)
  end
end

'render'については、下記のように上で書きました。

レタリングするテンプレートを指定する。
デフォルトはアクション名(コントローラのメソッド名)と同じViewファイルを使用してHTMLを作成するが、
renderで使いたいViewファイルを指定することができる。
エラーメッセージ表示のために、元の画面に差し戻す際などに利用する。

これをすごく簡単にまとめてしまうと、「ただビューの画面を出力するだけ」ということになります。
このメッセージコントローラーの例で言えば、「更新ボタン」を押さずにもう一回同じ画面を表示した、ということになりますね。

まとめ:データ保存の失敗時には、'redirect_to'ではなく'render'を利用した方が良いのはなぜか?

ここまで読めば、'redirect_to'と'render'の違いもなんとなく理解できるようになったのではないでしょうか?
'redirect_to'では、任意のビューに、 「新しくアクセスしなおす」 というような挙動をします。
データの保存が成功した場合は、その任意の画面が更新されていて欲しいので、この挙動は有効に働いてくれます。

しかし保存ができなかった場合は、データを更新する必要もないので、'redirect_to'を使用すると、余計な挙動が発生してしまうのです。
その点、'render'はビューを表示する 「だけ」 なので、データ保存が失敗した際には都合が良いわけです。
もちろんより細かい使い分けも必要だとは思いますが、'redirect_to'と'render'の違いを知るとっかかりにはなったかと思います。

参考資料

railsのrenderとredirect_toの違い
Railsでリダイレクトをするredirect_toメソッドの使い方 - Rails Webook
[Rails] renderとredirect_toの違い - 拝啓、シーシュポス

39
37
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
39
37