🐥

NoMethodError in Users#showのエラー解決

2024/01/03に公開

Railsチュートリアル第2章の演習問題で、以下のエラーが出た。

直面したエラー

NoMethodError in Users#show

Showing /workspaces/toy_app/app/views/users/_user.html.erb where line #14 raised:
undefined method `micropost' for #<User id: 1, name: "takashi", email: "takashi@abc.com", created_at: "2024-01-03 03:51:21.095067000 +0000", updated_at: "2024-01-03 03:54:10.553318000 +0000">

エラーの意味

UsersControllerのshowアクションで定義されていない「micropost」というメソッドが使われている、と指摘された。

やりたいこと

ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示したい。

何をしたらエラーが出たか

ユーザーのshowページのビューで、user.micropostでユーザーのマイクロポストを取得しようとしたらエラーが出た。

view/users/_user.html.rb
<div id="<%= dom_id user %>">
  <p>
    <strong>Name:</strong>
    <%= user.name %>
  </p>

  <p>
    <strong>Email:</strong>
    <%= user.email %>
  </p>

#ここでエラーが出た
  <p>
    <strong>micropost:</strong>
    <%= user.micropost %>
  </p>

</div>

まだ理解しきれていないが、本来ユーザーのshowページのビューはuser.show.html.erbであるが、部分テンプレートで_user.html.rbを呼び出している。

view/users.show.html.erb
<p style="color: green"><%= notice %></p>
#部分テンプレート
<%= render @user %>

<div>
  <%= link_to "Edit this user", edit_user_path(@user) %> |
  <%= link_to "Back to users", users_path %>

  <%= button_to "Destroy this user", @user, method: :delete %>
</div>

エラー時のUsersControllerのshowアクションは以下のとおり。

UsersController.rb
class UsersController < ApplicationController
  before_action :set_user, only: %i[ show edit update destroy ]
  
  def show
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def user_params
      params.require(:user).permit(:name, :email)
    end
end

やったこと

  • ユーザーモデルとマイクロポストモデルの関連付けがされているか確認
  • user.micropostからuser.micropostsに変更
  • user.micropost.firstに変更
  • user.micropost.first.contentに変更

ユーザーモデルとマイクロポストモデルの関連付け

マイクロポストとユーザーの関連付けをしていなかったので行った。

models/user.rb
class User < ApplicationRecord
  has_many :microposts
end
models/Micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
end

ただこれを行っただけではエラーは解決しなかった。

user.micropostからuser.micropostsに変更

ビューのユーザーのマイクロポストを取得するところをuser.micropostからuser.micropostsに変更した。

_user.html.rb
  <p>
    <strong>micropost:</strong>
    #以下をuser.micropostsに変更した
    <%= user.micropost %>
  </p>

Railsチュートリアルの解説に以下の説明があり、

first_user.micropostsを実行することで、最初のユーザーが投稿したすべてのマイクロポストを取得します。

ユーザーモデル(テーブル)とマイクロポストモデル(テーブル)は1対多の関係だから、micropostではなくmicropostsとすべきと考えた。

変更した結果、エラーは出なくなったが、以下の表示になった。

micropost: #<Micropost::ActiveRecord_Associations_CollectionProxy:0x00007efdd1816f48>

user.micropost.firstに変更

micropost: #<Micropost::ActiveRecord_Associations_CollectionProxy:0x00007efdd1816f48>

この表示の意味はまだ理解しきれていないが、おそらくどのマイクロポストなのか特定していないのが原因ではないかと考え、ユーザーの最初のマイクロポストを表示するため、.firstを付け足した。

変更した結果、エラーは出なくなったが、以下の表示になった。

micropost: #<Micropost:0x00007efdd1844380>

user.micropost.first.contentに変更

micropost: #<Micropost:0x00007efdd1844380>

この表示の意味はまだ理解しきれていないが、おそらくまだ指定が足りないのが原因ではないかと推測した。

そもそもマイクロポストの内容がどうなっているかを確認した。
マイクロポストのビューを見てみると、ContentUserがあるとわかった。

_micropost.html.erb
<div id="<%= dom_id micropost %>">
  <p>
    <strong>Content:</strong>
    <%= micropost.content %>
  </p>

  <p>
    <strong>User:</strong>
    <%= micropost.user_id %>
  </p>

</div>

今回はユーザーのshowページに、ユーザーの最初のマイクロポスト(の内容)を表示したいので、.contentを付け足した。

その結果、ユーザーのshowページに、ユーザーの最初のマイクロポスト(の内容)を表示することができた。

micropost: Railsチュートリアルで学習中です

解決時の内容は以下のとおり。

_micropost.html.erb
  <p>
    <strong>micropost:</strong>
    <%= user.microposts.first.content %>
  </p>

エラーが生じた原因

今回エラーが生じた原因は、UsersControllerのshowアクションで定義されていない「micropost」というメソッドが使われている、だった。

最初は、UsersControllerのshowアクションにメソッドを追記すべきなのかと考えたが、メソッドにはRailsのコントローラーで定義する以外にも存在すると知った。

firstメソッド:モデルのコレクションから先頭のレコードを取得する。

モデルのコレクション.first(取得する件数=nil)

エラーは出なくなったが、.microposts.contentはどういう類のメソッドで、user.microposts.first.contentを完全に説明できるようになっていないので、メソッドの理解を深めていきたい。

参考記事

https://diveintocode.jp/blogs/Technology/RailsMethod01

Discussion