LoginSignup
2
2

More than 3 years have passed since last update.

HTTPリクエストとrenderの違いを知らないと痛い目に合う話

Last updated at Posted at 2019-11-11

ミナミ(@minami_nakasato)です。

今日も出来たてホヤホヤのエラーを共有します。

経緯

作成中のチャットアプリの構成からご紹介。

まず、ユーザーやチャットグループの登録・編集・削除をするページがいくつか。

(これらには特に問題ありません)

それを除けば、残りはたった1ページ↓


上段

グループ名とグループメンバーの名前を列挙する部分

中段

グループメンバーの投稿内容を表示する部分

下段

グループメンバーが投稿するフォーム


これら3つの要素を1ページにまとめたシンプルなアプリだ。

ログイン後には当ページが表示される。

そこで、ページ上段において「グループメンバーの表示」をするにあたり、コントローラにこんなコードを書いた↓

messages_controller.rb
def index
  @message = Message.new
  @messages = @group.messages.includes(:user)
  @members = @group.users
end

そしてビューファイルの上段の部分はこう↓

index.html.haml
.header
 .header__box
  - @members.each do |member|
   = member.name
  end

データベースには「特定のグループと紐づいたユーザー」が1人〜たくさん登録されていて、彼らの名前はeachメソッドで一人一人表示される。

こんな具合に↓

スクリーンショット 2019-11-11 23.01.36.png

そして、link_toredirect_to などHTTPリクエストをルーティングに届けるメソッドを使って、当たり前にページを表示することができた。

今度は、実際にメッセージを投稿してみよう↓

投稿する

同じ画面が投稿フォームも兼ねているので、コントローラのcreateアクションにはこう書いた↓

messages_controller.rb
  def create
    @message = @group.messages.new(message_params)
    if @message.save
      redirect_to group_messages_path(@group)

「グループテーブルと紐づいているメッセージテーブルのカラムに、新しい投稿内容を保存する」

という、ごく普通のActive Recordメソッドだ。

そして、投稿が成功した際には

「redirect_to」

で元のページに戻る。

「redirect_to」が行う処理はHTTPリクエスト。

つまり、

  1. HTTPメソッドとURIがルーティングに届き、仕事の命令は対応するコントローラ(messages_controller)ひいてはアクション(index)に届く。

  2. indexアクションは定義されたインスタンス変数にしたがって行動。データベースからデータを読み取り、その結果をビューに反映。

  3. 上記のレスポンスがブラウザに届き、無事に新しい投稿内容が元の画面が表示される。

という一連の処理だ。

テキストを打ち込み投稿し、無事に期待通りの処理が行われた。

(非同期通信は未実装)

問題はここから。

エラー発生

このアプリには、「フォームに何も書き込まない状態では投稿が保存されない」バリデーションをモデルファイル内でかけている。

内容はこの通り↓

message.rb
class Message < ApplicationRecord
  belongs_to :group
  belongs_to :user
  validates :content, presence: true
end

そして上記のcreateアクション内のif文はこう続く。

messages_controller.rb
def create
    @message = @group.messages.new(message_params)
    if @message.save
      redirect_to group_messages_path(@group)
    else
      @messages = @group.messages.includes(:user)
      render :index
    end
  end

投稿の保存に失敗した時は、

「render」

を使って元のページのビューファイルを呼び出す魂胆だった。

バリデーションが機能するかテストするために、何もテキストを入力せずに投稿ボタンを押してみたところ……

スクリーンショット 2019-11-08 22.55.39.png

「undefined method `each' for nil:NilClass」

つまり

「nil(何もない)というクラスに対してeachというメソッドは定義されていませんよ」

とのお叱り。

「グループメンバーをeachで列挙しろと言われても、そもそも何もないのに何かを列挙するなんて出来ませんやん」

というわけだ。

ここでのrenderの仕事はHTTPリクエストを送ることではなく、単に「指定のビューを表示すること」だけ。

indexアクションだけでなく、ビューで表示すべきデータが代入されたインスタンス変数を、createアクション内にも定義しなければ、エラーが出るのは当然だ。

そこで

@members = @group.users

を追加して

messages_controller.rb
def create
    @message = @group.messages.new(message_params)
    if @message.save
      redirect_to group_messages_path(@group)
    else
      @members = @group.users
      @messages = @group.messages.includes(:user)
      render :index
    end
  end

に変更したところ、無事に解決。

なんてことないミスなのだが、デバッグツールをかますまで気付けなかった。

binding.pryよ、生まれてきてくれてありがとう。

そして当惑させてごめんね、renderメソッドちゃん。

======================================
ミナミ(@minami_nakasato)です。都内のベンチャー企業でwebデザイン/プログラミング/動画撮影や編集などをやっています。

2
2
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
2
2