質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

バリデーション

Validationとは特定の入力データが、求められた条件に当てまっているかをチェックするために使われます。

Q&A

解決済

1回答

7596閲覧

rails undefined method `each' for nil:NilClassの解決法

toryoto

総合スコア1

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

バリデーション

Validationとは特定の入力データが、求められた条件に当てまっているかをチェックするために使われます。

2グッド

2クリップ

投稿2021/09/18 15:26

前提・実現したいこと

railsにて本の感想アプリを作成しています。
投稿機能と一覧機能を同画面に表示させ、さらにバリデーションを付けたところデータが空白な場合エラーが発生してしまいました。解決法やソースコードをご教授いただきたいです

発生している問題・エラーメッセージ

NoMethodError in Books#create
Showing /home/ec2-user/environment/bookers/app/views/books/index.html.erb where line #13 raised:

undefined method `each' for nil:NilClass

該当のソースコード

#コントローラ
class BooksController < ApplicationController
def top

end

def index
@books = Book.all
@book = Book.new
end

def create
@book = Book.new(book_params)
if @book.save
flash[:notice] = "Book was successfully created."
redirect_to book_path(@book.id) #セーブできた時
else
render :index
end
end

def show
@book = Book.find(params[:id])
end

def edit
@book = Book.find(params[:id])
end

def update book = Book.find(params[:id])
if book.update(book_params)
flash[:notice] = "Book was successfully updated."
redirect_to book_path(book.id)
else
render :show
end
end

def destroy
book = Book.find(params[:id])
book.destroy
flash[:notice] = "Book was successfully destroyed."
redirect_to books_path
end

private

def book_params
params.require(:book).permit(:title, :body)
end
end

#index.html.erb

<h1>Books index</h1> <table> <thead> <tr> <th>Title</th> <th>Body</th> <th colspan="3"></th> </tr> </thead>
<tbody> <% @books.each do |book| %> <tr> <td><%= book.title %></td> <td><%= book.body %></td> <td><%= link_to "Show", book_path(book) %></td> <td><%= link_to "Edit", edit_book_path(book) %></td> <td><%= link_to "Destroy", book_path(book), method: :delete, "data-confirm" => "Are you sure?" %></td> </tr> <% end %> </tbody> </table>
<h2>New book</h2>

<%= form_with model:@book, url:'/books', local:true do |f| %>

<% if @book.errors.any? %>
<%= @book.errors.count %> prohibited this book from being saved: %>
<ul>
<% @book.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>

</div> <% end %>

<label for="book_title">title</label>
<%= f.text_field :title %>
<label for="book_body">body</label>
<%= f.text_area :body %>
<%= f.submit '投稿' %>
<% end %>

#バリデーション
class Book < ApplicationRecord
validates :title, presence: true
validates :body, presence: true
end

試したこと

いろいろと試したのですができませんでした。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

kuuumi, ss_s👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

まずエラーメッセージを見てみると、 /home/ec2-user/environment/bookers/app/views/books/index.html.erb where line #13と表示されています。これはつまりindex.html.erbの13行目でエラーが起きているということです。
また、undefined method `each' for nil:NilClassと言われています。これは「nilにはeachというメソッドが存在しません」というエラーです。これは、変数が意図せずnilになってしまったときによく起こります。

次に、index.html.erbの13行目を見てみましょう。……と思ったのですが、貼り付けていただいたコードが1行分ずれているようです。eachが書かれているのは12行目なので、おそらくエラーが起きているのはこの行でしょう。12行目には <% @books.each do |book| %>が書かれています。

さて、先程このエラーは「nilにはeachというメソッドが存在しません」という意味だと説明しました。これを今回のケースに当てはめてみましょう。今回のケースでは、@booksという変数に対して、eachメソッドを呼び出そうとしています。
ここで@booksには本来Book.allの結果が代入されているはずです。Book.allの結果はeachメソッドを持っているので、Book.allの結果が代入されていれば、このエラーは起きません。
ところが今回はエラーが起きていますね。それは、@booksBook.allの結果ではなくnilが入っているためです。そのため、@books.eachnilに対してeachを呼び出そうとしてしまい、「nilにはeachというメソッドが存在しません」というエラーが起きてしまっていました。

ではなぜ@booksnilが入っていたのでしょうか。
これはcontrollerのコードを見ると分かります。挙げていただいたコードから、createアクションのコードを抜粋します。

ruby

1def create 2 @book = Book.new(book_params) 3 if @book.save 4 flash[:notice] = "Book was successfully created." 5 redirect_to book_path(@book.id) #セーブできた時 6 else 7 render :index 8 end 9end

createアクションでは、@book.saveに失敗したときに、render :indexを実行しています。
render :indexは、index.html.erbの内容をrenderします。つまり、今回のエラーは@book.saveが失敗したときに起きているものだと分かります。

render :indexの意味をもう少し詳しく説明します。render :indexは、「このアクションで代入したインスタンス変数(@が先頭についた変数)を使って、index.html.erbをrenderする」という意味です。このとき、indexアクションの定義(def indexの中身)はrender :indexには関係してきません。
今回のエラーが起きているケースでは、createアクションで@bookインスタンス変数のみが代入されています。

ところがindex.html.erbでは、@bookインスタンス変数に加えて、@booksインスタンス変数も参照しています。つまり、@booksインスタンス変数には何も代入していないのに、参照してしまっています。
Rubyでは代入をしていない@booksインスタンス変数はnilが入ります。つまり、index.html.erb内の@books.eachというコードは、今回のケースではnilに対してeachメソッドを呼び出そうとしていました。

ここまででエラーの原因は説明できました。次に解決方法です。
解決方法は単純で、@booksに適切な値をセットしてあげれば大丈夫です。
今回のケースではおそらくindexアクションの定義と同じように、@books = Book.allを足してやれば良いでしょう。つまりcreateアクションの定義は次のようになります。

ruby

1def create 2 @book = Book.new(book_params) 3 if @book.save 4 flash[:notice] = "Book was successfully created." 5 redirect_to book_path(@book.id) #セーブできた時 6 else 7 @books = Book.all # この行を追加 8 render :index 9 end 10end

このように修正するとおそらくエラーが出ないようになると思います。


なおこの問題とは直接関係ありませんが、質問するときはコードの部分をコードブロックとして書くとわかりやすくなっておすすめです。コードブロックにするには、"`"3つでコードブロックにしたい範囲を囲みます。
詳しくは質問画面の左下にヘルプがあるようなので、そちらを参照してください。

ヘルプのスクリーンショット

投稿2021/09/19 14:33

pocke

総合スコア41

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toryoto

2021/09/21 03:58

エラー発生の原因から解決法まで丁寧にありがとうございました。 また、質問が見にくく申し訳ございませんでした。 これから質問する際は、わかりやすくなるようにしたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問