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

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

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

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Q&A

解決済

1回答

2532閲覧

Reviewを投稿できない

hrc

総合スコア55

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

0グッド

0クリップ

投稿2016/07/05 16:33

レストランクチコミサイトを作ろうとしています。
レストランページからReviewを投稿できるようにしたいのですが
undefined local variable or method `review_create_path'
とエラーが出て投稿できません。

以下ViewとRoute定義とControllerですが、何が問題なのでしょうか?

/app/views/restaurant/show.html.slim

ruby

1 = form_for :post, url: review_create_path do |f| 2 = f.label :title, "title" 3 = f.text_field :title 4 = f.label :comment, "comment" 5 = f.text_area :comment 6 = f.submit 7

route.rbはこちらです。

ruby

1 # Locale Information 2scope "(:locale)", locale: /en|ja/ do 3 get '/' => 'frontpage#index' 4 get 'restaurant/' => 'restaurant#index' 5 get 'restaurant/:id' => 'restaurant#show' 6 get 'menu/' => 'menu#index' 7 get 'menu/:id' => 'menu#show' 8 get 'area/' => 'area#index' 9 get 'area/:id' => 'area#show' 10 11 resources(:review) 12 post 'review/' => 'review#create', as: :review_create_path 13 14 devise_for :users, skip: [:omniauth_callbacks] 15 16end 17

rake routesの結果はこうでした。

ruby

1 review_index GET (/:locale)/review(.:format) review#index {:locale=>/en|ja/} 2 POST (/:locale)/review(.:format) review#create {:locale=>/en|ja/} 3 new_review GET (/:locale)/review/new(.:format) review#new {:locale=>/en|ja/} 4 edit_review GET (/:locale)/review/:id/edit(.:format) review#edit {:locale=>/en|ja/} 5 review GET (/:locale)/review/:id(.:format) review#show {:locale=>/en|ja/} 6 PATCH (/:locale)/review/:id(.:format) review#update {:locale=>/en|ja/} 7 PUT (/:locale)/review/:id(.:format) review#update {:locale=>/en|ja/} 8 DELETE (/:locale)/review/:id(.:format) review#destroy {:locale=>/en|ja/} 9 review_create_path POST (/:locale)/review(.:format) review#create {:locale=>/en|ja/}

こちらはコントローラーです。

ruby

1class ReviewController < ApplicationController 2 def create 3 @review = Review.new(review_params) 4 respond_to do |format| 5 if @review.save 6 format.html { redirect_to @review, notice: 'Review was successfully created.' } 7 format.json { render :show, status: :created, location: @review } 8 else 9 format.html { render :new } 10 format.json { render json: @review.errors, status: :unprocessable_entity } 11 end 12 end 13 end 14 15 def review_params 16 params.require(:review).permit(:title, :comment) 17 end 18end

初歩的ですみませんが、どうかよろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

routes.rbの記述が間違ってます。

そもそも、resources :reviewで指定しているので、createアクションはすでに定義されています。
提示いただいた以下の部分は、rake routesの結果かと思いますが、2行目にばっちり書かれています。

Ruby

1review_index GET (/:locale)/review(.:format) review#index {:locale=>/en|ja/} 2 POST (/:locale)/review(.:format) review#create {:locale=>/en|ja/} 3# 以下略

呼び出し側では、

Ruby

1<%= form_for :post, review_path, method: :post do %>

というように呼び出します。(すみません、slim苦手です)

ちなみに、review_create_pathでundefined errorが出るのは、asオプションの指定が間違っているからです。

Ruby

1post 'review/' => 'review#create', as: :review_create_path 2# これだと、review_create_pathというメソッドが割り当てられていて、 3# helperから呼び出すときは、review_create_path_pathと呼び出す事になる 4 5# どうしてもこの形式にこだわるのであれば、以下のようにします。 6resources :review, except: [:create] 7# パス指定が重複するので、通常のresourcesでreview POSTのパスが生成されないようにする 8post 'review/' => 'review#create', as: :review_create 9# これで、ヘルパでreview_create_path method: :postで無事review#createが呼び出される

特別な理由がない限り、デフォルトのresourcesで生成されるパスを利用される事を勧めます。
デフォルトで生成されるパスは、RESTfulなパスですから、出来るだけそれに従う方がわかりやすいと思います。

routesの設定に関しては、Reilsガイドに詳しく乗っていますから、こちらを参考にされると良いでしょう。

コメントでのやりとりで、ネストしたリソースでパスを生成する方法の話題で、以下の返答を頂いたので、ちょっと追記します。

URLは/restaurant/(id)のままで当該レストランに対するReviewを投稿できるようにしたい

一つのリソース(Restaurant)にReviewを含むその他のリソースを操作するパスは、もちろん作成可能なのですが、この場合、どんどんRestaurantControllerが肥大化してゆく問題があります。
また、リソース(Model)と操作(Controller)が直結していないと、その処理がどこで行われているのかわかりにくくなる可能性もあります。
ただ、他にもいろいろな要件が絡んでくるので、どちらが正解ともいえないので、ケースバイケースで使い分ける事になるかと思います。

Ruby

1# リソースをネストさせない場合 2 3# routes.rb 4 5resources :restaurant do 6 member do 7 post 'create_review' 8 patch 'update_review' 9 delete 'destroy_review' 10 end 11end 12# この状態なら、以下のパスが生成されるはず 13# restaurantのCRUDは省略 14# create_review_restaurant POST /restaurant/:id/create_review restaurant#create_review 15# update_review_restaurant PATCH /restaurant/:id/update_review restaurant#update_review 16# delete_review_restaurant DELETE /restaurant/:id/destroy_review restaurant#destroy_review 17 18# ヘルパを使って呼び出すときはこんな感じ 19<%= form_for :review, create_review_restaurant(@restaurant) do %><% end %> 20<%= form_for :review, update_review_restaurant(@restaurant, review_id: @review), method: :patch do %><% end %> 21<%= link_to 'delete', destroy_review_restaurant(@restaurant, review_id: @review), method: :delete do %><% end %>

この場合、全てRestaurantControllerで処理するので、引き回す:idは@restaurantのものです。
update_reviewアクションとdestroy_reviewアクションでは、パスの設定には記述されていませんが、review_idをパラメータに含めて引き回す必要が出てきます。

一方、ネストしたりソースを使う場合は以下のようになるかと思います。

Ruby

1# リソースをネストさせる場合 2 3# routes.rb 4 5resources :restaurant do 6 resources :review, only: [:create, :update, :destroy] 7end 8# 生成されるパスはこんな感じ 9# restaurantのCRUDは省略 10# restaurant_reivew POST /restaurant/:restaurant_id/review review#create 11# restaurant_review PATCH /restaurant/:restaurant_id/review/:id review#update 12# restaurant_reivew DELETE /restaurant/:restaurant_id/review/:id review#destroy 13 14# 呼び出すときはこう 15<%= form_for :review, restaurant_review_path(@restaurant, @review) do %><% end %> 16<%= form_for :review, restaurant_review_path(@restaurant, @review) do %><% end %> 17<%= link_to 'delete', restaurant_review_path(@restaurant, @review), method: :delete %> 18# もっとすっきりした書き方もありますが、わかりやすさ優先で。詳しくはRailsガイドを見てください

見比べてみるとわかるかと思いますが、ネストしたりソースとして表現した方が、すっきりしている上、責任の所在もわかりやすいかと思います。

RailsはRailに乗っている状態だとすっきりわかりやすい記述になるように出来ています。
つまり、なんだかアクロバティックな事をしている、あるいは泥臭い処理をしている状態というのは、Railから外れてきているという事になります。

状況によっては、あえてRailから外れてやらねばならない場合も出てくるかもしれませんが、ごく一般的な処理の場合はあえてRailから外れる必要はなく、Railに乗って(Railsの推奨するやり方に従って)書き進める方が、よりわかりやすく、より書きやすい状態を維持出来ると思います。

ソースを書いていて、なんだか怪しいにおいがしてきたら、ドキュメントなどを調べてみて、本来のやり方が内かどうかをチェックすると、最終的に楽をしてきれいなソースを書けるかと思います。

投稿2016/07/06 10:58

編集2016/07/08 01:17
rifuch

総合スコア1901

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

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

hrc

2016/07/06 15:27

ありがとうございます!上記の設定をしたところ syntax error, unexpected ',', expecting keyword_end resources(:review), except: [:create] ^ →resources(:review), except: [:create]のところです となりました。
rifuch

2016/07/06 15:40

すみません。()の所間違ってました。 resources :review, except: [:create] が正解です。 回答の方も修正しておきました。
hrc

2016/07/06 15:46

いえいえーすみません。動きました!が、レストランごとにreviewを付与する関係でIDを付け足さないといけないので resources :review, except: [:create] post 'review/:id' => 'review#create', as: :review_create としたところ No route matches {:action=>"create", :controller=>"review", :id=>#<Review id: nil, restaurant_id: nil, user_id: nil, food_rating: nil, decor_rating: nil, service_rating: nil, title: nil, image1: nil, comment: nil, created_at: nil, updated_at: nil>, :locale=>:en} missing required keys: [:id] → form_for @review, url: review_create_path(@review) do |f| (ビューの箇所) となってしまいました。この場合はroute.rbに何と記述すればいいのでしょうか?
rifuch

2016/07/06 17:07

reviewに関連づけるレストランのIDは、restaurant_idというパラメータを渡すのでは? form_for :post, review_path(restaurant_id: @restaurant.id), method: :post do あと、特別な理由がない限り、routesに複雑な設定をするのではなく、デフォルトのresources :reviewで生成される方のパスを利用される事をお勧めします。 それから、routesの詳細について参考となるページのリンクをつけておくので、そちらを参考にしてみてください。
hrc

2016/07/06 23:50

ありがとうございます!Viewのform_forは form_for :post, review_path(restaurant_id: @restaurant.id), method: :post do にしてみましたが wrong number of arguments (3 for 1..2) となってしまいます。最後に|f|とか入れても同じです。ちなみにRails 4.2.3を使っております。バージョンで書き方違いますかね? 別件というかそもそもの話になるのですが > あと、特別な理由がない限り、routesに複雑な設定をするのではなく、デフォルトのresources :reviewで生成される方のパスを利用される事をお勧めします。 こちらは全く同意でして、そのようにしたいところですが、今回はRestaurantページに直接Reviewを書き込めるようにしたいという趣旨で考えております。きっと本来はReviewのnewに相当するページに遷移させるべきなんだろうと思うのですが、ユーザビリティ考えたらこの方が手軽で投稿数が上がるイメージが有ります。投稿したReviewの編集・削除もRestaurantページから行いたい感じです。 その場合、RouteやController、Viewの設定はどのようにすべきなのでしょうか?Railsでの流儀がわからなくて初歩的なことを聞いてしまいますがよろしくお願いします。
rifuch

2016/07/07 02:35

form_forの引数は、モデルオブジェクト、オプションの順でした。 form_for @post, url: review_path(restaurant_id: @restaurant.id), method: :post do ですね。 > 今回はRestaurantページに直接Reviewを書き込めるようにしたい なるほどです。であれば、リソースを利用したルーティングの設定で、リソースのネストを表現する方が自然でしょう。 resources :restaurants do resources :review end この場合、処理するコントローラはReviewControllerになりますが、フォームを表示させるviewと、実処理をするcontrollerは同一である必要はないという事です。 このとき、reviewのフォームや、リスト表示する部分のviewをreviewの方に記述し、Restaurantのviewからpartialで呼び出してあげると、コードが追いかけやすくなりますよ。 (create,update,destroyあたりはAjax処理するでしょうし)
hrc

2016/07/07 05:50

なるほどですね!ありがとうございます。ちょっと頂いたアドバイスに沿ってやってみます。また、結果をお知らせします。
hrc

2016/07/07 23:48

resources :restaurants do resources :review end をroute.rbに書いてみたところ以下のようになりました。 restaurant_review_index GET (/:locale)/restaurants/:restaurant_id/review(.:format) review#index {:locale=>/en|ja/} POST (/:locale)/restaurants/:restaurant_id/review(.:format) review#create {:locale=>/en|ja/} new_restaurant_review GET (/:locale)/restaurants/:restaurant_id/review/new(.:format) review#new {:locale=>/en|ja/} edit_restaurant_review GET (/:locale)/restaurants/:restaurant_id/review/:id/edit(.:format) review#edit {:locale=>/en|ja/} restaurant_review GET (/:locale)/restaurants/:restaurant_id/review/:id(.:format) review#show {:locale=>/en|ja/} PATCH (/:locale)/restaurants/:restaurant_id/review/:id(.:format) review#update {:locale=>/en|ja/} PUT (/:locale)/restaurants/:restaurant_id/review/:id(.:format) review#update {:locale=>/en|ja/} DELETE (/:locale)/restaurants/:restaurant_id/review/:id(.:format) review#destroy {:locale=>/en|ja/} これだとrestaurant->reviewのURL階層構造にする必要があるのでしょうか? URLは/restaurant/(id)のままで当該レストランに対するReviewを投稿できるようにしたいと考えております。どのようなやり方すればいいでしょう?何度もすみません。
rifuch

2016/07/08 00:35

URL設計の問題ですね。 Restaurantに関係する、reviewの処理も含めてRestaurantControllerで処理するのであれば、ネストしたリソースのルートを作成する必要はないです。 しかし、これをやり始めると、どんどんRestaurantControllerが肥大化していく懸念があります。 わざわざネストしたリソースのルートを作って、それぞれのコントローラを分けるのはそのためです。 コメント欄だとソースが見にくいので、回答欄の方に追記します。
hrc

2016/07/08 01:17

ありがとうございます!お手数をお掛けします。
hrc

2016/07/08 11:49

遅くなってすみません。頂いた通りroute.rbに記述した上で = form_for :review, create_review_restaurant(@restaurant) do とViewに書いたところ undefined method `create_review_restaurant' for xxx となりました。もしかしてrouteに何か書き足す必要ありましたでしょうか?
hrc

2016/07/08 11:53

Railsの記載に従ったほうがいいというのは非常に納得しておりましてそのようにしたいとは思っております。ただやりたいことはこのTeratailのコメント入力みたいなことを考えております。ログイン状態であれば誰でも投稿可能というイメージです。ここだとURLは https://teratail.com/questions/40195 となっていますが、このIDは元々の質問に紐付いていて、コメントには紐付いていないと考えられます。このようなアプリを作る際にはRailsでは特殊な設定になってしまうのでしょうか?
rifuch

2016/07/08 12:23

> undefined method `create_review_restaurant' for xxxとなりました。 すみません、ヘルパの呼び出しに関しては、ちょっと想像で書いている部分があって、実際のヘルパが何という名前で呼ぶ出すのかは、rake routesで確認してみてください。 >ここだとURLはhttps://teratail.com/questions/40195となっていますが、 コメント欄の方のフォームのパス、コメントの編集のパスも同じですが、hidden_fieldに別なidが設定されており、さらにデータタイプを指定するためのname属性も設定されています。(多分、投稿の方は回答のID、編集の方はコメントのID) teratailはRailsで実装されているのではないと思いますが、これが見通しが良いかどうかは、判断が分かれるところではないかと思います。 とかくURL設計は設計者の好みや考えが反映される部分ではあるので、そこは好きなようにやってみれば良いと思いますが、好きなようにやるのであれば尚更、パスの生成等に関してよく勉強する必要があると思います。
hrc

2016/07/08 12:27

なるほど!承知しました。考え方の問題ってことですね。ありがとうございました。もう少し自分で頑張ってみます。
rifuch

2016/07/08 12:31

URL設計には正解がない、ともいえますが、わざわざRailsで自動的にパスを生成するための仕組みあるのには理由がある、という事です。 この辺は、「どうしてわざわざそんなものを作っているのだろう?」と考えてみるのは良い事だと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問