129

我有一个关于 form_for 和嵌套资源的两部分问题。假设我正在编写一个博客引擎,并且我想将评论与一篇文章联系起来。我定义了一个嵌套资源,如下所示:

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

这会给出一个错误,“Called id for nil,这会错误地等等。” 我也试过

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

它正确呈现,但将 f.text_area 与文章的“文本”字段而不是评论相关联,并在该文本区域中显示 article.text 属性的 html。所以我似乎也有这个错误。我想要的是一个表单,它的“提交”将调用 CommentsController 上的创建操作,参数中有一个 article_id,例如对 /articles/1/comments 的发布请求。

我的问题的第二部分是,创建评论实例的最佳方法是什么?我在 ArticlesController 的 show 动作中创建了一个 @comment,所以一个评论对象将在 form_for 助手的范围内。然后在 CommentsController 的创建操作中,我使用从 form_for 传入的参数创建新的@comment。

谢谢!

4

3 回答 3

233

特拉维斯 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 所说的可能适用于文章的上下文,但它在其他各种方面对我不起作用。

有很多关于嵌套资源的讨论,例如http://weblog.jamisbuck.org/2007/2/5/nesting-resources

有趣的是,我刚刚了解到大多数人的单元测试实际上并没有测试所有路径。当人们遵循 jamisbuck 的建议时,他们最终有两种获取嵌套资源的方法。他们的单元测试通常会得到/发布到最简单的:

# 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

对不起,答案很长。我认为没有多少人意识到其中的微妙之处。

于 2011-01-06T05:08:29.113 回答
56

确保在 controller:@post@commentpost 中创建两个对象,例如:

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

然后在视图中:

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

请务必在 form_for 中明确定义数组,而不仅仅是像上面那样用逗号分隔。

于 2010-12-27T07:35:59.557 回答
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页面。

于 2010-01-10T00:08:52.133 回答