Rails コントローラーの create アクションと update アクションで並んでいる redirect と render ですが、どう違うのかいまいち分からなかったので調べました。学び始めたばかりの頃はあまり気にしていませんでしたが、先日2つの違いを質問されてうまく答えられなかったので。。。
####結論
【render】
controller から指定された view を直接呼び出す。
動き: controller → view
データなどの状態はそのままで画面だけ貼り替える
【redirect_to】
http リクエストを受けた場合と同じ処理
動き: controller → URL → route → controller → view
指定したURLに一からアクセスしてデータをすべて取得し直す
####実際にやってみた
①通常の controller ファイルの create アクションで動きを見てみる
def create
@task = Task.new(task_params)
if @task.save
redirect_to tasks_path, notice: "タスクを作成しました"
else
render :new
end
end
バリデーションに引っかかって保存されないようにタスク名は空欄。これで"Create Task" を押すと、、、
保存されず、"render :new"が実行されました。入力したデータはそのままです。
② render を redirect に置き換えてみた。Prefixで render の場合と同じく new.html.erb を呼び出すように指定します。
def create
@task = Task.new(task_params)
if @task.save
redirect_to tasks_path, notice: "タスクを作成しました"
else
redirect_to new_task_path #render を redirect_to に置き換えた
end
end
今回も、わざとタスク名を空欄で保存。"Create Task" を押すと、、、
入力していた分が全て空欄になってしまいました。今回はテストで文字数が少ないから問題ありませんが、文字数の多いブログを投稿しようとしてエラーが起きた場合、書いた文章が水の泡ということになってしまいます。
何が起きているのか、困った時の Rails ガイドです。
####Railsガイドの説明
render と redirect_to について、 Railsガイドの説明は下記。
Railsガイド レイアウトとレンダリング 2.3 redirect_toを使用する
renderはレスポンス構成時にどのビュー (または他のアセット) を使用するかを指定するためのものです。redirect_toメソッドは、この点においてrenderメソッドと根本的に異なります。redirect_toメソッドは、別のURLに対して改めてリクエストを再送信するよう、ブラウザに指令を出すためのものです。
Railsガイド レイアウトとレンダリング 2.3.2 renderとredirect_toの違い
redirect_toを実行した後、コードはそこで実行を終了し、ブラウザからの次のリクエストを待ちます (通常のスタンバイ状態)。その直後、redirect_toでブラウザに送信したHTTPステータスコード302に従って、ブラウザから別のURLへのリクエストがサーバーに送信され、サーバーはそのリクエストを改めて処理します。それ以外のことは行っていません。
ちょっと分かりにくいですが。。。
もう一つこちらの記事も大変参考にさせていただき、何が起きているか分かってきました。
render と redirect の違い
render は入力した内容を保持しつつ、指定された view を直に呼び出して画面を張り替える。redirect_to は http リクエストを改めて要求して、route や controller を通って指定された view を一から呼び出し直しているということのようです。
ちなみに、もう一つ試してみました。
③ redirect_to を render に変えてみる
タスクの保存が成功したら index アクションに移動する設定にしてみました。
def create
@task = Task.new(task_params)
if @task.save
render :index
else
render :new
end
end
今度はタスクが保存されるように、タスク名も含めて全ての項目を入力。
これで Create Taskを押してみると。。。
エラーが発生しました。これは index.html.erb の インスタンス変数 @task に何も入っていないためと思われます。@task は controller の index アクションで生成されます。つまり指定された view を呼び出そうとしているものの、controller の index アクションを経由していないのでインスタンス変数が作成されていない。そのためエラーが発生していると思われます。
一方でredirect_toを使ってみると。
def create
@task = Task.new(task_params)
if @task.save
redirect_to tasks_path
else
render :new
end
end
保存されるように、全ての項目を入力。
Create Task を押すと。。
タスクが保存され、 index の view に移動してくれました。