web-dev-qa-db-ja.com

ネストされたリソースを含むform_for

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アクションを呼び出すフォームで、paramsにarticle_idが含まれる、たとえば/ articles/1/commentsへのポストリクエストです。

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

ありがとう!

117
Dave Sims

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?" %>

Jamuraaの言うことは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

長い回答で申し訳ありません。多くの人がその微妙さに気づいていないと思います。

219
cdunn2001

コントローラーで作成された両方のオブジェクトを持っていることを確認してください:@post@comment投稿用、例えば:

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

次にビューで:

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

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

52
Travis Reeder

フォームで特別なことをする必要はありません。 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 %>

デフォルトでは、このコメントはcreateCommentsControllerアクションに移動します。このアクションにredirect :backを挿入すると、Articleページに戻ります。 。

33
jamuraa