概要
-
作成中のオリジナルアプリの開発中に、
render
とredirect_to
の挙動の違いによるエラーが発生したので、両者の違いについて調べた
renderについて
- レスポンス構成時にどのビュー (または他のアセット) を使用するかを指定するためのもの
- 同じコントローラか異なるコントローラかを問わず、任意のテンプレートを表示させることができる
- 同じコントローラのアクションのテンプレートを出力する場合には、アクション名を指定するだけで良い
- 異なるコントローラのアクションのテンプレートを出力する場合には、app/viewsを起点とするフルパスを渡すことで表示できる
def update
@hoge = Hoge.find(params[:id])
if @hoge.update(hoge_params)
redirect_to(@hoge)
else
render "edit" #シンボル(:edit)でも可
end
end
render "foos/show"
Railsでは、パスにスラッシュが含まれていると異なるコントローラの配下にあると認識されるため、上記のような方法で異なるコントローラのテンプレートを表示させることができるようです。
redirect_toについて
- 別のURLに対して改めてリクエストを再送信するよう、ブラウザに指令を出すためのもの
- アプリケーションで現在どのページが表示されていても、任意のインデックス表示ページにリダイレクトすることができる
redirect_to hoge_url
renderとredirect_toの違い
-
render
は指定されたテンプレートを描画するだけでアクション自体は実行されないが、redirect_to
は実行されると現在のURLとは異なるURLに新たにリクエストがサーバーに送信され、サーバーが改めてそのリクエストを処理するためアクションまで実行される
上記の挙動の違いにより、インスタンス変数を使う場合などで問題が起こるときがあるようです。(*今回私がハマったのはここでした)
renderとredirect_toの違いによるエラーの発生
前提
-
hoges_controller.rb
とfoos_controller.rb
がある -
hoges_controller.rb#index
でfoos_controller.rb#create
を実行してデータ登録させる -
foos_controller.rb#creat
でデータの登録が成功・失敗した場合のどちらもhoges/index
を表示させたい
*シンプルにするため実際の実装とは異なる部分あるため、変なところあるかもですがご容赦を
def index
@foo_all = Foo.all
end
def create
@foo = Foo.new(foo_params)
if @foo.save
flash[:success] = "登録に成功しました"
redirect_to hoges_url
else
flash[:danger] = "登録に失敗しました"
render hoges_path <-ここが問題
end
end
ここではrender hoge_path
としてhoges/index.html.erb
テンプレートを描画させようとしていますが、先にも書いたように、render
ではアクションを実行しません。
そのためhoges_controller.rb#index
のなか定義しているインスタンス変数@foo_all = Foo.all
が実行されず、このインスタンス変数を使ってる部分がNoMethodError
となってしまいました。
対処法その1: redirect_toを使う
-
redirect_to
を使えば、ブラウザから改めてサーバー側へリクエストが送信されるので、hoges_controller.rb#index
に記述したコードも正常に実行される
def create
@foo = Foo.new(foo_params)
if @foo.save
flash[:success] = "登録に成功しました"
redirect_to hoges_url
else
flash[:danger] = "登録に失敗しました"
redirect_to hoges_url <-ここ
end
end
この方法が一番シンプルかもしれませんが、リクエストの送信が増えることになるため、ある程度の規模感までアプリが大きくなる場合には遅延などの問題の原因になり得ます。
対処法その2: renderの前でインスタンス変数を定義しておく
-
render
の前にあらかじめ必要となるインスタンス変数を定義しておくことで、アクションを実行しないrender
を使う場合でも問題なく動作させることができるようになる
def create
@foo = Foo.new(foo_params)
if @foo.save
flash[:success] = "登録に成功しました"
redirect_to hoges_url
else
foo_all = Foo.all <- 追加
flash[:danger] = "登録に失敗しました"
render hoges_path
end
end
この方法を使えばredirect_to
のようにサーバーへリクエストを送信しないので、1つ目の方法よりも遅延が発生しにくくなります。
ただし、コード量が増えてきた時にはこの部分の記述量も増えたり、複雑化しそうな気もするので、そのあたりはまた考える必要がありそう?