こんにちは。
この前部分テンプレート関連でちょっと想定外のエラーに出くわしたので、記事にしてみました。
実行環境は以下の通りです
- Ruby3.1.0
- Rails7.0.2.2
どんな問題?
別リソースのコントローラからviewファイルをrenderするとき、renderするviewファイルの中で部分テンプレートを呼び出している時に発生する問題です。
簡単にいうと部分テンプレートの呼び出し方には少し注意しましょうということですが、文字の説明だけだととても分かりにくいのでイメージとコードで説明していきます
具体的なコードとエラー内容
例えば店舗の詳細ページで、ステータス変更の処理があったとします。
さらに、詳細ページには他の店舗一覧の表示をしているとします(部分テンプレートで実装)
↓以下イメージ
詳細ページのコードは以下のようになってます
↓コントローラ
class ShopsController < ApplicationController
# 他のアクションは略
def show
@shop = Shop.find(params[:id])
@related_shops = Shop.order(created_at: :desc).limit(3).where.not(id: @shop)
end
end
↓showのviewファイル
p style="color: green" = notice
== render @shop
= button_to 'openにする', shop_shop_status_path(@shop), method: :patch, class: 'btn btn-primary btn-sm'
div.mt-2
= link_to "Edit this shop", edit_shop_path(@shop)
' |
= link_to "Back to shops", shops_path
= button_to "削除する", @shop, method: :delete, class: 'btn btn-danger btn-sm mt-2'
div.mt-3 style="background-color: #eee;"
p 部分テンプレートエリア
/ ここに注目
= render partial: 'shop', collection: @related_shops
↓shopの部分テンプレート
div id="#{dom_id shop}"
p
strong 店舗名:
= shop.name
そしてステータス変更のコントローラは以下のようにしていたとします
class ShopStatusesController < ApplicationController
def update
@shop = Shop.find(params[:shop_id])
if @shop.update(status: 'open')
redirect_to @shop, notice: 'Openに変更しました'
else
@related_shops = Shop.order(created_at: :desc).limit(3).where.not(id: @shop)
render 'shops/show', status: :unprocessable_entity
end
end
end
「ステータス変更だけならrender処理する必要ないじゃん」という意見が聞こえてきそうですが、いったんスルーしてください。例えば日付入力などが必要だとして入力値を保持させたいケースなどを想定していて、説明用にシンプルな構造にするためにこのようにしています(単に面倒だったということもありますが)
このコードで普通にステータス変更に成功する場合は、問題なく動作します
試しに失敗のケースの動作チェックをしてみましょう(以下のようにして強制的に失敗処理を実行)
- if @shop.update(status: 'open')
+ if @shop.update(status: 'open') && false
Missing partial shop_statuses/_shop
と言われているので、どうやら更新処理をするコントローラ名と同じshop_statuses
ディレクトリ内でテンプレートファイルを探しているようです。
個人的にはshops/show.html.slim
のviewファイル内で呼び出している部分テンプレートなんだから、viewファイルの呼び出し元がどこであれ部分テンプレートはshops/_shop.html.slim
を読み込むはずと思っていましたがそうではないみたいですね
解決策
ちゃんとパスを指定すれば直ります
- = render partial: 'shop', collection: @related_shops
+ = render partial: 'shops/shop', collection: @related_shops
補足ですが、collectionオプションの省略形で書くとなぜか問題なく動作しました
= render @related_shops
Rails6のアプリでやったときはこの形ではダメだった気がするんですが、7からよしなにやってくれるようになったんですかね(うーん謎🤔)
Rails6だとダメだったよーとか、省略形だと内部的にどんな動きをしてるよーとか補足情報あればコメントいただけると嬉しいです。
さいごに
エラー内容を読むとなんてことはない問題なんですが、こういう仕様になっているという参考になれば幸いです。