2010-01-09 162 views
106

我有一个关于form_for和嵌套资源的两部分问题。假设我正在编写一个博客引擎,我想将评论与一篇文章联系起来。我如下界定的巢资源: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 %> 

这给出一个错误,“被调用的id为零,这会错误地等。”我也试着

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

哪个正确呈现,但涉及f.text_area文章的“文本”字段,而不是评论的,​​并提出了在该文本区域article.text属性的HTML。所以我似乎也有这个错误。我想要的是一个表单,其'submit'将在CommentsController上调用create操作,在params中带有article_id,例如对/ articles/1/comments的发布请求。

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

谢谢!

回答

207

特拉维斯R是正确的。 (我希望我可以upvote你。)我刚刚得到这个工作自己。有了这些路线:在

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

发送到控制器

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

只是因为它说在http://guides.rubyonrails.org/routing.html#nested-resources,没有特殊的命名空间:

resources :articles do 
    resources :comments 
end 

你得到这样的路径。

但部分和形式变得棘手。请注意括号:

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

最重要的是,如果你想有一个URI,你可能需要的东西是这样的:作为http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects

描述

[@article, @comment] 

article_comment_path(@article, @comment) 

或者

例如,在与comment_item suppli编辑迭代,

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

jamuraa说什么可以在文章的背景下工作,但它不适用于我以其他各种方式。

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

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

# 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 

对不起,长答案。我想,没有多少人知道这些微妙之处。

+0

这是工作,但我必须像jamuraa说的那样修改控制器。 – 2014-05-17 15:26:27

+0

果酱的方式很有效,但您最终可能会遇到您可能不知道的其他路线。最好是明确的。 – cdunn2001 2014-06-28 19:13:32

+0

我嵌套了资源,@result在@course里面。尽管'[@result,@ course]'有效,但'form_for(@result,url:{action:“create”})'也可以。这只需要最后的型号名称和方法名称。 – Anwar 2015-09-04 05:57:54

33

你不需要在表格中做特殊的事情。你只要建立正确的注释在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 %> 

默认情况下,此评论会去的CommentsControllercreate行动,那么您可能会想要放入redirect :back,因此您将路由回Article页面。

+9

我不得不使用'form_for([@ article,@new_comment])'格式。我认为这是因为我正在显示'comments#new'的视图,而不是'article#new_comment'。我在'article#new_comment'中找到了Rails是否足够聪明,可以计算出注释对象所嵌入的内容,并且您不必指定它? – Soup 2012-05-05 08:28:23

51

一定要在控制器中创建两个对象:@post@comment的职位名称,例如:

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

然后在视图:

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

一定要在明确定义数组form_for,而不仅仅是像上面那样逗号分开。

+0

Travis's是一个旧的答案,但我相信它是最正确的Rails 3.2.X.如果您希望表单构建器的所有元素都填充“注释”字段,则只需使用数组,则不需要url帮助器。 – Karl 2013-04-17 04:05:48

+1

只设置操作嵌套的父对象。如果你只是部分嵌套资源(例如按照例子),那么设置父对象将导致form_for失败(刚刚用rails 5.1重新确认) – iheggie 2017-07-23 19:44:37