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

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

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

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

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

Q&A

解決済

2回答

2107閲覧

【Rails】参加申請の承認機能で、ActionController::UrlGenerationErrorが出てしまう

MASA_H

総合スコア4

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

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

1グッド

0クリップ

投稿2021/09/22 04:27

編集2021/09/22 04:35

前提・実現したいこと

最終的に実装したいのは、ユーザーの参加申請を承認する機能です。

〜実現したい承認機能の流れ〜
①ユーザーAが管理するチームに、別のユーザーBが参加の応募申請をする。
②ユーザーAが申請待ち一覧を確認して、申請の承認or却下を選ぶ。
③参加を承認した場合、参加承認リストにuserが追加され、申請待ち一覧から削除する。
④参加を却下した場合、申請待ち一覧から削除する。

ユーザーB視点で見ると、「応募したチームの一員になる」のではなく、「チームの練習に参加する承認をもらう」イメージです。

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

申請一覧画面(applies#index)を読み込む段階で、下記のエラーが発生しています。
イメージ説明

該当のソースコード

ルーティング

Rails.application.routes.draw do devise_for :users root to: "teams#index" resources :teams do resources :applies, only: [:index, :create, :destroy] resources :approves, only: [:create, :destroy] end resources :users, only: :show end

rails routes
イメージ説明

エラーページへの遷移元(チーム詳細ページ)
teams/showからは、次のことが可能です。
①「ユーザーBはチームへの応募ができる」(applies#createへ)
②「ユーザーBはチームへの応募を取り消せる」(applies#destroyへ)
②「チーム管理者は申請一覧ページへの遷移できる」(applies#indexへ)

app/views/teams/show.html.erb

1 <% if user_signed_in? && current_user.id != @team.user_id %> 2 <div class="team-apply"> 3 <% if @team.applied?(current_user) %> 4 <%= link_to "このチームへの応募を取り消す", team_apply_path(@team.id), method: :delete %> 5 <% else %> 6 <%= link_to "このチームに応募する", team_applies_path(@team.id), method: :post %> 7 <% end %> 8 </div> 9 <% end %> 10 11 <% if user_signed_in? && current_user.id == @team.user_id %> 12 <%= link_to "申請承認待ち一覧ページ", team_applies_path(@team.id) %> 13 <% end %>

該当のエラーページ(参加申請一覧ページ)
applies/indexでは、次のことが可能です。
①「そのチームへの応募状況の確認」
②「チーム管理者は、申請の承認ができる」(approves#createへ)
③「チーム管理者は、申請の却下ができる」(approves#destroyへ)
→ここの記述をすると、applies#indexの読み込み段階でエラーが発生!

app/views/applies/index.html.erb

1 <div class="team-contents"> 2 <h2 class="title"><%= current_user.nickname %> さんのチームへの応募申請</h2> 3 <ul class="team-lists"> 4 <% @applies.each do |apply| %> 5 <li class="list"> 6 #省略 7 <%= link_to "参加を却下する", team_approfe_path(user_id: apply.user_id, team_id: apply.team_id), method: :delete %> 8 <%= link_to "参加を承認する", team_approves_path(user_id: apply.user_id, team_id: apply.team_id), method: :post %> 9 </li> 10 <% end %> 11 </ul> 12 </div>

appliesコントローラー

class AppliesController < ApplicationController before_action :authenticate_user! def index @applies = Apply.where(team_id: params[:team_id]) end def create @team_apply = Apply.new(user_id: current_user.id, team_id: params[:team_id]) if @team_apply.save redirect_to team_path(params[:team_id]) end end def destroy @team_apply = Apply.find_by(user_id: current_user.id, team_id: params[:team_id]) @team_apply.destroy redirect_to team_path(params[:team_id]) end end

approvesコントローラー

app/controllers/approves_controller.rb

1class ApprovesController < ApplicationController 2 3 def create 4 approve = Approve.new(user_id: approve_params[:user_id], team_id: approve_params[:team_id]) 5 approve.save 6 redirect_to root_path 7 end 8 9 def destroy 10 apply = Apply.find_by(user_id: approve_params[:user_id], team_id: approve_params[:team_id]) 11 apply.destroy 12 redirect_to root_path 13 end 14 15 private 16 def approve_params 17 params.permit(:user_id, :team_id) 18 end 19end

試したこと

・試しにエラー元の「参加を却下する」に関連するlink_toメソッドを削除すると、applies#indexの画面は正常に表示されました。

・現時点では、approves#createにおける「承認したユーザーを申請一覧から削除」の記述もうまく実装することができていません。

・「approves#destroyのパスの引数に、idが見つからない」という旨のエラー文かと思い、apply.idを引数に渡してみましたが、同じくUrlGenerationErrorが出てしまいました。

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

プログラミング初心者ゆえ、至らない記述も多いかと思いますが、どうか皆様のお力添えを頂ければと思います。

shinoharat👍を押しています

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

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

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

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

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

guest

回答2

0

今回の場合、「user_id: apply.user_id, team_id: apply.team_idの部分で、
第一引数に渡したapplyのidを特定している」という解釈で合っていますでしょうか?

いえ、そういうわけではありません。
以下、解説です。

URLヘルパーによるURLの生成について(destroy編)

まず、 routes.rb で resources をネストした場合のURLについて説明します。
以下の部分をピックアップして考えます。

rb

1 resources :teams do 2 resources :approves, only: [:create, :destroy] 3 end

上の設定によって生成される approves のルーティングは以下の2つです。

Prefix Verb URI Pattern Controller#Action team_approves POST /teams/:team_id/approves(.:format) approves#create team_approfe DELETE /teams/:team_id/approves/:id(.:format) approves#destroy

注目してほしいのは URI Pattern の部分で、ここを見れば URL 生成に必要なパラメーターが判断できます。

--

approves#destroy の URL について具体的に見てみます。
destroy の URI Pattern は /teams/:team_id/approves/:id です。コロン( : )で始まる部分がパラメーターですので、この URL を生成するためには :team_id:id が必要なことが分かります。両方のパラメーターがなければ URL を生成できず、足りなければ UrlGenerationError が発生してしまいます。

rails console コマンドで実際に動かしてみましょう。(rails console では app. を頭につけるとURLヘルパーが試せます)

irb

1$ rails console 2 3irb(main)> app.team_approfe_path(id: 111, team_id: 222) 4=> "/teams/222/approves/111" 5 6irb(main)> app.team_approfe_path(id: 111) 7ActionController::UrlGenerationError (..., missing required keys: [:team_id]) 8 9irb(main)> app.team_approfe_path(team_id: 222) 10ActionController::UrlGenerationError (..., missing required keys: [:id])

このように、URI Pattern で指定されたパラメーターが1つでも足りなければエラーとなります。

--

URI Pattern で指定されたパラメーター以外のパラメーターを追加することも可能です。
その場合、追加分のパラメーターは URL の後ろにくっつきます。

irb

1irb(main)> app.team_approfe_path(id: 111, team_id: 222, user_id: 333, zzz: 444) 2=> "/teams/222/approves/111?user_id=333&zzz=444"

--

パラメーター :id は、ハッシュではなく、第一引数で渡すこともできます。

irb

1### どちらも同じURLが生成される ### 2 3irb(main)> app.team_approfe_path(id: 111, team_id: 222) 4=> "/teams/222/approves/111" 5 6irb(main)> app.team_approfe_path(111, team_id: 222) 7=> "/teams/222/approves/111"

また、数値ではなくモデルインスタンスを渡すこともできます。

irb

1irb(main)> approve = Approve.find(111) 2irb(main)> app.team_approfe_path(approve, team_id: 222) 3=> "/teams/222/approves/111"

--

ここまでの説明で、以下の記述だとエラーとなり、

team_approfe_path(user_id: apply.user_id, team_id: apply.team_id)

以下の記述だと動いてくれる理由が分かるかなと思います。

team_approfe_path(apply, user_id: apply.user_id, team_id: apply.team_id)

また、URLヘルパーに渡したインスタンスは、単にidの数値が使われるだけですので、

今回の場合、「user_id: apply.user_id, team_id: apply.team_idの部分で、
第一引数に渡したapplyのidを特定している」という解釈で合っていますでしょうか?

この段階で何かを特定するとか、そういう動きはしません。

URLヘルパーによるURLの生成について(create編)

ついでなので、create の URL も見てみましょう。

Prefix Verb URI Pattern Controller#Action team_applies POST /teams/:team_id/applies(.:format) applies#create team_approves POST /teams/:team_id/approves(.:format) approves#create

URI Pattern を見ると、どちらも必要なパラメーターは :team_id のみです。なので、URLヘルパーには以下のように引数を渡します。

team_approves_path(team_id: 111) #=> "/teams/111/approves"

また、必要なパラメーターが一種類のみの場合、単に数値のみを渡してもOKです。

# 必要なパラメーターが1種類しかないので、第一引数は :team_id として解釈される team_approves_path(111) #=> "/teams/111/approves"

実際のコードを見てみると、「参加申請一覧」ページでは、 team_id: 111 の指定方法が取られています。

erb

1<%= link_to "参加を承認する", team_approves_path(user_id: apply.user_id, team_id: apply.team_id), method: :post %>

一方、「チーム詳細」ページでは、数値のみを渡す方法が取られています。

erb

1<%= link_to "このチームに応募する", team_applies_path(@team.id), method: :post %>

どちらも正しい方法ですので、ご安心ください。
URLヘルパーの引数指定は色々なパターンがある、という話がしたかっただけです。

ちなみに、実は他にも指定の仕方があります。全部のパターンを解説すると、回答が無限に長くなりますので、割愛します。暇なときに、Railsガイドのルーティングのページを読んでみると、理解が深まると思います。
https://railsguides.jp/routing.html

ここまで書いて気づいたのですが

ここまで書いて気づいたのですが、以下のリンクは UrlGenerationError になりそうな気がします。
また動作確認してみてください。

erb

1<%= link_to "このチームへの応募を取り消す", team_apply_path(@team.id), method: :delete %>

URL と params について

解説に戻ります。
コントローラーでは、URL 文字列に含まれるパラメーターを params で参照できます。

例えば、

/teams/222/approves/111?user_id=333

というURLの場合、コントローラーでは以下の params を利用できます。

params[:id] #=> 111 params[:team_id] #=> 222 params[:user_id] #=> 333

実際のコードでは、以下のように params を使っていますね。

rb

1class ApprovesController < ApplicationController 2 3 ... 4 5 def destroy 6 apply = Apply.find_by(user_id: approve_params[:user_id], team_id: approve_params[:team_id]) 7 apply.destroy 8 redirect_to root_path 9 end 10 11 ... 12 13end

実装を見ると分かるのですが、 params[:id] は使われていません。

下記の第一引数である apply は、

team_approfe_path(apply, user_id: apply.user_id, team_id: apply.team_id)

/teams/:team_id/approves/:id の :id を指定するために与えたんでしたね。
でもコントローラーでは params[:id] を使っていない。
つまり、 apply は**「別に使わないけど、URLを生成するために仕方なく指定した」**という形になっています。

いえ、安心してください。Rails で resources をネストさせると、こういうことは割とよくあります。別に問題ありません。

--

一番最初の疑問に立ち返ります。

今回の場合、「user_id: apply.user_id, team_id: apply.team_idの部分で、
第一引数に渡したapplyのidを特定している」という解釈で合っていますでしょうか?

URLの生成時点では、「単にidの数値が使われるだけ」でしたね。
そして、コントローラーで削除対象の Apply を特定する処理では、 :user_id:team_id を使っています。
ここでも apply は使われていません。

もう一度いいます。
今回のケースでは、「別に使わないけど、URLを生成するために仕方なく指定した」です。

--

以上です。
何か分からない点があれば、またコメントください。

投稿2021/09/22 15:16

shinoharat

総合スコア1685

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

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

MASA_H

2021/09/22 16:17

夜遅くにも関わらずここまでご丁寧に解説して頂き本当にありがとうございます。 おかげさまで、知った気になっていた知識も正しい方向へ学び直すことができました! shinoharat様の解説と、リンク先のRailsガイドをしっかり読み込んでよりしっかりとした情報を吸収して参ります。 貴重なお時間を頂戴し、改めて深く、感謝申し上げます。 ※ちなみに「UrlGenerationErrorになりそう」とご指摘頂いていた部分は、なぜか期待通りの挙動をしておりました(データベース上も削除が機能していました)。今回の知識と照らし合わせて自分でも検証してみようと思います!
guest

0

ベストアンサー

・「approves#destroyのパスの引数に、idが見つからない」という旨のエラー文かと思い、apply.idを引数に渡してみましたが、同じくUrlGenerationErrorが出てしまいました。

エラーの解釈も対処方もそれで合っているはずですが・・・
以下のように、第一引数に apply のインスタンスを渡してもエラーになるでしょうか?

diff

1- team_approfe_path(user_id: apply.user_id, team_id: apply.team_id) 2+ team_approfe_path(apply, user_id: apply.user_id, team_id: apply.team_id) 3

投稿2021/09/22 05:17

編集2021/09/22 11:55
shinoharat

総合スコア1685

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

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

MASA_H

2021/09/22 05:34

ご指摘の通り、第一引数に`apply`を渡したところ、期待通りの挙動になりました! 昨日丸一日考えて分からず、勇気を出して質問してよかったです…ありがとうございます! 大変厚かましいと承知の上、もしご教授頂ければ幸いなのですが… 今回の場合、「`user_id: apply.user_id, team_id: apply.team_id`の部分で、 第一引数に渡した`apply`のidを特定している」という解釈で合っていますでしょうか?
shinoharat

2021/09/22 05:53

ごめんなさい。詳しく書くと長くなりそうなので、今日の夜(または明日の日中)に書かせてください。 私の回答もちょっと良くないところがあるので、そこも含めて改めて解説します。
MASA_H

2021/09/22 05:59

承知致しました!お忙しいところ、大変申し訳ございません。 もしお時間取れるタイミングがあるようでしたら、何卒ご教授頂けますと幸いです。
shinoharat

2021/09/22 15:16

コメント欄だとテキスト装飾ができず説明しづらかったので、解説を回答として投稿しました。 質問者さんの理解度がどの程度か分からなかったので、かなり噛み砕いた説明になってます。 なので、めっちゃ長くなってしまいました。。。 もしかしたら「そんくらい知っとるわ!」みたいな部分もあるかもしれませんが、最後まで読んでもらえると嬉しいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問