【Rails】undefined method ***_path の原因と解決のための前提知識の徹底解説【初心者向け】

Ruby on Rails

undefined method ***_path というエラーが出て動作しません・・・

こんな問題を解決します!

原因

undefined method ***_path

は翻訳すると

未定義のメソッド ***_path


(ただし、*** のところは場合によっていろんな文字列がきます)

つまり***_path というメソッドが定義されていないのに呼び出そうとしているのが原因です。

解決方法

undefined method 〜 は大きく2つの可能性があります。

  • 定義されていない → 定義すればいい
  • 定義されているけど呼び出し側に誤字がある→誤字をなおす

しかし、***_path というメソッドは Rails では特別なメソッドで、定義の仕方を理解しておく必要があります。

通常のメソッドは def 〜 end で実装しますが、***_path というメソッドは config/routes.rb にルーティングを追加することで追加されます。

そのあたりを以下で解説していきます。

前提知識: ルーティング定義で追加されるメソッド

前に書いたとおり、Ruby や Rails では def 〜 end ではない方法でメソッドを追加する方法がたくさんあります。

config/routes.rb はそのうちの一つです。

undefined method ***_path というエラーを解決できない初学者の場合、このメソッドがどのようなルールで追加されるのかが理解できていないことが多いです。

ここでは、前提知識としてルーティングとそこで自動的に追加されるメソッドの最低限の知識について解説します。

ルーティング定義の読み方

例えば、config/routes.rb を以下のように記述して

Rails.application.routes.draw do
  root to: 'posts#index'
end

ルーティング定義を確認しましょう

以下のコマンドで確認できます。

$ rails routes
Prefix Verb URI Pattern Controller#Action
  root GET  /           posts#index

本記事では Prefix と URI Pattern の列を見ます。

Prefix は追加されるメソッドの接頭辞(Prefix) とそのメソッドがどのような戻り値に成るのか、をあらわしています。

追加されるメソッドは

  • Prefix + _path
  • Prefix + _url

の2つです。

この例の場合は

  • root_path
  • root_url

の2つのメソッドが追加されるよ!ということを表しています。

そしてそれぞれのメソッドの戻り値は URI Pattern を見ます。

この例の場合は

  • メソッド root_path の戻り値は “/” という文字列
  • メソッド root_url の戻り値は “https://〜/” という文字列

となります。

これが rails routes コマンドの読み方です。(他の行については本記事の範囲外なので省略します)

引数を持つメソッド

以下ように記述してみましょう。

Rails.application.routes.draw do
  resources :posts
end

ルーティング定義を見ると以下のようにたくさん追加されます。

$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PATCH  /posts/:id(.:format)      posts#update
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy

以下の行に着目してみましょう。

URI Pattern が :id となっています。

このような行は /posts/1 や /posts/2 など :id のところに別の数字(や文字列)が来ることを表しています。

このような場合は post_path で “/posts/:id” という文字列が返ってくるのではありません。

メソッドの引数に渡して

post_path(id: 1)

のように呼び出すと “/posts/:id” という文字列が返り、

post_path(id: 2)

と呼び出すと、 “/posts/:id” という文字列が返ります。

引数にモデルのインスタンスを渡す

一般には /posts/:id というパスの :id の部分はモデルの id を入れることが多いため

# @post という変数に Post モデルのインスタンスが入った状態で
post_path(id: @post.id)

という形で呼ばれることになります。

ただし、これには省略形があり、

# @post という変数に Post モデルのインスタンスが入った状態で
post_path(@post)

とモデルのインスタンスを直接渡してあげれば直接 :id の部分にそのインスタンスの id が入れられることが決まっています。

前提知識ルーティング定義で追加されるメソッドのまとめ

一旦まとめます。

  • config/routes.rb でルーティングを定義するとメソッドが自動的に追加される
  • rails routes コマンドで確認できる
  • 具体的には Prefix + _path、Prefix + _url というメソッドが追加される
  • 戻り値に :id のようなものを含む場合は引数もある

いかがでしょうか。

以上がルーティングの基礎です。

前提知識: 追加されたメソッドの使い方

ルーティングで追加されたメソッドはパスやURLを返すため、使う場面としては大きく

  • リンク
  • フォーム
  • リダイレクト

の3種類があります。

undefined method ***_path が発生している箇所もほぼこの辺りでしょう。

上記3つでの使われ方を解説していきます。

リンク

リンクの最終形は

<a href="パス">xxx</a>

のような a タグを作ることです。

***_path のようなメソッドは a タグの href 属性を作るために使用します。

最もよく使うメソッドは link_to でしょう。

link_to の基本形

以下のようなルーティングのときに

$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
    posts GET    /posts(.:format)          posts#index

以下のように link_to の第2引数として使います。

<%= link_to 'xxx', posts_path %>

ルーティング定義より、posts_path が ‘/posts’ という文字列を返すので、これは

<%= link_to 'xxx', '/posts' %>

と同じです。

そして link_to メソッドは

  • 第1引数: テキスト要素
  • 第2引数: href属性

となるため、実行されると

<a href="/posts">xxx</a>

となります。

モデルを渡すこともできる

link_to に渡す第2引数は基本的には(パスやURLを表す)文字列なのですが、モデルを渡すこともできます。

<%= link_to 'xxx', model %>

変数 post に Post クラスのインスタンスが渡されている場合、

<%= link_to 'xxx', post_path(model) %>

と書いたのと同じ動作となります。

post_path となっているのは “モデル名 + _path” となるように link_to が自動で処理してくれるためです。

もし、Abc クラスだった場合は

<%= link_to 'xxx', abc_path(model) %>

を実行することになります。

フォーム

フォームは最終的に form タグを作るのが目的です。

<form action="パス" method="...">
</form>

最近の Rails でよく使うのは form_with です。

form_with を使うと form タグを作ることができます。

url: を指定する場合

form_with の第一引数に url を使う場合に ***_path メソッドを使用します。

例えば、以下のようなルーティング定義のときは

$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
    posts POST    /posts(.:format)          posts#index

以下のように記述すると…

<%= form_with(url: posts_path) do |f| %>
<% end %>

以下のようなHTMLが生成されます。

<form action="/posts" accept-charset="UTF-8" method="post">
〜省略〜
</form>

ポイントは action が posts_path メソッドの戻り値である “/posts” という文字列になっているところです。

モデルのインスタンスを渡す場合

link_to と同様にモデルのインスタンスを渡すこともできます。(というかそちらのほうがメインです)

モデルのインスタンスを渡す場合は model: で指定します。

<%= form_with(model: Post.new) do |f| %>
<% end %>

を実行すると

<form action="/posts" accept-charset="UTF-8" method="post">
〜省略〜
</form>

のように action が “/posts” になっているのが分かると思います。

仕組みを説明する前にもう一つ例を出します。

ルーティング定義に PUT /posts/:id の行を追加しました。

$ rails routes
   Prefix Verb   URI Pattern               Controller#Action
    posts POST    /posts(.:format)          posts#index
     post PATCH   /posts/:id(.:format)      posts#update

このときに

<%= form_with(model: Post.first) do |f| %>
<% end %>

を実行すると

<form action="/posts/1" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch" />
〜省略〜
</form>

となり、action が “/posts/1” となります。

この違いは、model に渡したインスタンスの id によります。

以下のルールで動作することになっています。

model に渡したインスタンスの id メソッドを呼び出した結果・・・

  • nil が返ってきたら「モデル名 + s_path」というメソッドを呼び出し、その戻り値をaction に設定する
  • nil 以外が返ってきたら「モデル名 + _path」というメソッドに model に渡したインスタンス自信を渡し、その戻り値を action に設定する

つまり、

Post.new はできたてのインスタンスでまだデータベースに保存されていないために id が nil のため・・・

<%= form_with(model: Post.new) do |f| %>

は

<form action="<%= posts_path %>" method="post">

のようになります。

いっぽう、Post.first はデータベースから取得したレコードをインスタンス化するため id があるので・・・

<%= form_with(model: Post.first) do |f| %>

<form action="<%= post_path(Post.first) %>" method="post">
  <input type="hidden" name="_method" value="patch" />

のようになります。(厳密には引数で Post.first を実行するわけではないですがわかりやすさのため・・・)

大事なのは、

<%= form_with(model: ここ) do |f| %>

の「ここ」の部分にモデルのインスタンスを渡した場合は、内部的に ***_path というメソッドを呼び出しているということです。

リダイレクト

リダイレクトの場合は、URLを指定するため link_to とほぼ同じです。

ただし、***_path ではなく ***_url を指定します。(***_path でも動作するのですが ***_urlのほうがお作法的にベターです)

リダイレクトしたいときに Rails で使用するメソッドは redirect_to です。

redirect_to "URL文字列"

例えば

   Prefix Verb   URI Pattern               Controller#Action
    posts GET    /posts(.:format)          posts#index

のようなルーティング定義のときに

redirect_to posts_url

とすると、

redirect_to "https://〜〜〜.com/posts"

のように指定したのと同様となります。

また link_to と同様にモデルのインスタンスを渡すこともでき、

   Prefix Verb   URI Pattern               Controller#Action
     post GET    /posts/:id(.:format)      posts#show

のようなルーティング定義のときに

redirect_to Post.first

のように指定すると “https://〜〜〜/posts/1” (Post.first の戻り値の id が 1 の場合) となります。

まとめ

エラーメッセージ undefined method ***_path の解決方法と、解決のための前提知識を解説しました。

解決方法は

  • ***_path と言うメソッドが定義されていないわけなので
    • そのメソッドを定義するか
    • 打ち間違えている場合はなおす

です。

なおせない場合は前提知識が欠けて可能性があります。

以下の知識の不備がないか確認してみましょう。

  • ***_path は config/routes.rb の定義によって追加されるメソッドであること
  • link_to, form_with にモデルを渡す場合に、内部的に ***_path が呼び出されるルールがあること

ルーティングの理解は Rails の根幹なのでこの辺りはしっかりと理解しておきたいところです。

その他の Rails エラー関連記事はこちら

タイトルとURLをコピーしました