ネストされたリソースを持つform_for


125

form_forとネストされたリソースについて2つの部分からなる質問があります。ブログエンジンを作成していて、コメントを記事に関連付けたいとしましょう。ネストされたリソースを次のように定義しました:

map.resources :articles do |articles|
    articles.resources :comments
end

コメントフォームは、記事のshow.html.erbビュー内で、たとえば次のように記事自体の下にあります。

<%= render :partial => "articles/article" %>
<% form_for([ :article, @comment]) do |f| %>
    <%= f.text_area :text %>
    <%= submit_tag "Submit" %>
<%  end %>

これにより、「nilに対して呼び出されたIDが誤って呼び出される」などのエラーが発生します。私も試しました

<% form_for @article, @comment do |f| %>

これは正しくレンダリングされますが、コメントではなく記事の「テキスト」フィールドにf.text_areaを関連付け、そのテキスト領域のarticle.text属性のHTMLを表示します。だから私もこれが間違っているようです。私が欲しいのは、 'submit'がCommentsControllerのcreateアクションを呼び出すフォームです。たとえば、/ articles / 1 / commentsへの投稿リクエストなど、paramsにarticle_idがあります。

私の質問の2番目の部分は、最初にコメントインスタンスを作成する最良の方法は何ですか?ArticlesControllerのshowアクションで@commentを作成しているので、コメントオブジェクトはform_forヘルパーのスコープ内にあります。次に、CommentsControllerのcreateアクションで、form_forから渡されたパラメーターを使用して新しい@commentを作成します。

ありがとう!

回答:


228

Travis Rは正しいです。(私が賛成票を投じることができればと思います。)私はこれを自分で機能させました。これらのルート:

resources :articles do
  resources :comments
end

あなたは次のようなパスを取得します:

/articles/42
/articles/42/comments/99

のコントローラーにルーティング

app/controllers/articles_controller.rb
app/controllers/comments_controller.rb

http://guides.rubyonrails.org/routing.html#nested-resourcesに記載されているとおり、特別な名前空間はありません。

しかし、パーシャルとフォームは扱いにくくなります。角括弧に注意してください。

<%= form_for [@article, @comment] do |f| %>

最も重要なことは、URIが必要な場合は、次のようなものが必要になる可能性があります。

article_comment_path(@article, @comment)

または:

[@article, @comment]

http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objectsで説明されています

たとえばcomment_item、反復のために提供されたコレクションパーシャル内では、

<%= link_to "delete", article_comment_path(@article, comment_item),
      :method => :delete, :confirm => "Really?" %>

ジャムラが言うことはArticleの文脈ではうまくいくかもしれませんが、それは他の様々な点で私にはうまくいきませんでした。

ネストされたリソースに関連する多くの議論があります。たとえば、http//weblog.jamisbuck.org/2007/2/5/nesting-resources

興味深いことに、ほとんどの人の単体テストは実際にはすべてのパスをテストしているわけではないことを知りました。jamisbuckの提案に従うと、ネストされたリソースを取得する方法は2つになります。彼らのユニットテストは一般的に最も簡単なものに取得/投稿します:

# POST /comments
post :create, :comment => {:article_id=>42, ...}

彼らが好むかもしれないルートをテストするために、彼らはこのようにする必要があります:

# POST /articles/42/comments
post :create, :article_id => 42, :comment => {...}

私がこれから切り替えたときにユニットテストが失敗し始めたので、私はこれを学びました:

resources :comments
resources :articles do
  resources :comments
end

これに:

resources :comments, :only => [:destroy, :show, :edit, :update]
resources :articles do
  resources :comments, :only => [:create, :index, :new]
end

ルートが重複していて、いくつかの単体テストに失敗しても大丈夫だと思います。(なぜテストですか?ユーザーが重複を決して見ない場合でも、フォームは暗黙的にまたは名前付きルートを介してそれらを参照することがあります。)それでも、不必要な重複を最小限に抑えるために、これをお勧めします:

resources :comments
resources :articles do
  resources :comments, :only => [:create, :index, :new]
end

長い答えてすみません。微妙なことに気づいている人は少ないと思います。


それは仕事ですが、ジャムラが言ったように私はコントローラーを変更しなければなりませんでした。
マーカスベッカー

Jamの方法は機能しますが、おそらく知らない余分なルートが発生する可能性があります。明示的にすることをお勧めします。
cdunn2001

ネストされたリソース、@ course内の@resultがありました。でも、[@result, @course]うまくいきましたが、うまくいきましたform_for(@result, url: { action: "create" }) 。これには、最後のモデル名とメソッド名のみが必要です。
Anwar

@ cdunn2001ここで "@article"をこのように記述する必要がある理由とこれが何を意味するのか説明していただけますか?以下の構文は何をしますか?:<%= form_for [@article、@comment] do | f | %>
Arpit Agarwal

1
Travis / @ cdunn2001は正解です。重複せずにネストされたルートを使用している場合は、親とリソースの両方を設定しないでください。そうしないと、すべてのアクションがネストされていると見なされます。同様に、すべてをネストした場合は、常にAT.parentを設定します。また、ルートが部分的にネストされたキャンセルボタンのある共通フォームがある場合は、次のようなパスを使用して、設定したとおりに機能します(子の複数形に注意):<%= link_to 'Cancel'、parent_children_path(AT.parent || AT.child.parent)%>
iheggie

54

コントローラで作成された両方のオブジェクトを持ってしてください:@post@commentポストのために、例えば:

@post = Post.find params[:post_id]
@comment = Comment.new(:post=>@post)

次に表示:

<%= form_for([@post, @comment]) do |f| %>

上記のようにカンマで区切るのではなく、form_forで配列を明示的に定義してください。


Travisは少し古い答えですが、Rails 3.2.Xにはこれが最も正しいと思います。フォームビルダーのすべての要素にコメントフィールドを設定する場合は、配列を使用するだけで、URLヘルパーは必要ありません。
Karl、

1
アクションがネストされている親オブジェクトのみを設定します。リソースを部分的にのみネストした場合(例のように)、親オブジェクトを設定するとform_forが失敗します(rails 5.1で今すぐ再確認)
iheggie

35

フォームで特別なことをする必要はありません。showアクションでコメントを正しく作成するだけです。

class ArticlesController < ActionController::Base
  ....
  def show
    @article = Article.find(params[:id])
    @new_comment = @article.comments.build
  end
  ....
end

次に、記事ビューでフォームを作成します。

<% form_for @new_comment do |f| %>
   <%= f.text_area :text %>
   <%= f.submit "Post Comment" %>
<% end %>

デフォルトでは、このコメントはのcreateアクションに送られます。このアクションはCommentsController、おそらくページにredirect :back戻されるようにしたいでしょうArticle


10
私はそのform_for([@article, @new_comment])フォーマットを使わなければなりませんでした。これはcomments#new、ではなくの見解を示しているためだと思いますarticle#new_comment。私はarticle#new_commentRailsでコメントオブジェクトが何にネストされているかを理解するのに十分スマートであり、そのように指定する必要がないと考えていますか?
スープ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.