1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[memo]renderとredirect_toの挙動の違いによるエラー

Posted at

概要

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
redirect_to hoge_url

renderとredirect_toの違い

  • renderは指定されたテンプレートを描画するだけでアクション自体は実行されないが、redirect_toは実行されると現在のURLとは異なるURLに新たにリクエストがサーバーに送信され、サーバーが改めてそのリクエストを処理するためアクションまで実行される

上記の挙動の違いにより、インスタンス変数を使う場合などで問題が起こるときがあるようです。(*今回私がハマったのはここでした)

renderとredirect_toの違いによるエラーの発生

前提

  1. hoges_controller.rbfoos_controller.rbがある
  2. hoges_controller.rb#indexfoos_controller.rb#createを実行してデータ登録させる
  3. foos_controller.rb#creatでデータの登録が成功・失敗した場合のどちらもhoges/indexを表示させたい

*シンプルにするため実際の実装とは異なる部分あるため、変なところあるかもですがご容赦を

hoges_controller.rb
def index 
  @foo_all = Foo.all
end
foos_controller.rb

  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に記述したコードも正常に実行される
foos_controller.rb

  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を使う場合でも問題なく動作させることができるようになる
foos_controller.rb

  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つ目の方法よりも遅延が発生しにくくなります。

ただし、コード量が増えてきた時にはこの部分の記述量も増えたり、複雑化しそうな気もするので、そのあたりはまた考える必要がありそう?

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?