【Rails7】 名前空間付きコントローラにおけるlink_to 削除でRoutingErrorになった
管理者向けの商品削除機能を実施するために、名前空間付きコントローラー(今回のケースではadmin/products
)でlink_to
メソッドを使用しました。
この際、名前空間の存在を意識せずに実装するとRoutingError
になったので、原因と解消方法を投稿します。
目次
環境
背景
ディレクトリ構造:
app/ |-- controllers/ | |-- admin/ | | `-- products_controller.rb | `-- products_controller.rb |-- views/ | |-- admin/ | | `-- products/ | | `-- index.html.erb | `-- products/ | `-- index.html.erb ...
ルーティング:
namespace :admin do resources :products end resources :products, only: %i[index show]
コントローラ:
# app/controllers/products_controller.rb class ProductsController < ApplicationController def index @products = Product.all end def show @product = Product.find(params[:id]) end end # app/controllers/admin/products_controller.rb class Admin::ProductsController < ApplicationController before_action :set_product, only: %i[ edit update destroy ] # 中略 # def destroy @product.destroy redirect_to admin_products_path, flash: { notice: "#{@product.name}を削除しました"} end private def set_product @product = Product.find_by(id: params[:id]) end end
一般ユーザー向けにはdomain/products
で商品一覧ページを表示させ、管理者向けにdomain/admin/products
で管理画面内の商品一覧ページを表示させ、そこで削除機能を実装します。
エラーの内容
今回、管理画面内の商品一覧ページに下記のような削除リンクを設けました。
app/views/admin/products/index.html.erb
<% @products.each do |product| %> ~~ <%= link_to product, data: { turbo_method: :delete, turbo_confirm: '削除してもよろしいですか?'} %> ~~ <% end %>
link_to の引数に、インスタンスを渡し、turbo_method: :delete
を指定することで、destroy
アクションの実行を期待しました。
しかし、以下のようなエラーが出てしまいました。
ActionController::RoutingError (No route matches [DELETE] "/products/[:id]"):
ルーティングも設定しているのに、ルーティングエラーが発生とは・・・。
エラーの原因
エラーの内容をよく見ると[DELETE] "/products/[:id]")
ということで、admin/products/[:id]
ではなく、一般ユーザー向けに作成したproductsのパスになっていました。
そもそも、link_to
メソッドの引数にモデルオブジェクト(インスタンス)を渡すと、そのオブジェクトのshow
アクションにマッピングされるURL(例えば /products/1
)が生成されます。
そのため、link_to
メソッドでは引数にデルオブジェクト(インスタンス)を渡し、turbo_method: :delete
を指定するだけで、特定の商品を削除できるはずでした。
しかし、上記のコードでは、名前空間を考慮せずに標準の商品コントローラーへのパスを構築してしまうため、admin
配下の商品コントローラにルーティングされるよう明示的に記述する必要があったようです。
link_to
があまりにもスマートな機能だからといって、考えなしにインスタンスを指定するのはよくありませんでした。。。
解決策
この問題を回避するためには、link_to
メソッドに明示的にパスを指定する必要があります。
具体的には、以下の2通りがあります
# 名前空間を明示的に指定したパスヘルパー <%= link_to admin_product_path(product), data: { turbo_method: :delete, turbo_confirm: '削除してもよろしいですか?'} %>
# ポリモーフィックURLヘルパー <%= link_to [ :admin, product ], data: { turbo_method: :delete, turbo_confirm: '削除してもよろしいですか?'} %>
参考:https://runebook.dev/ja/docs/rails/actiondispatch/routing/polymorphicroutes
いずれにしても、admin
配下のproductsコントローラを使うことを明示的に指定しないといけません。