【问题标题】:form_for with nested resourcesform_for 嵌套资源
【发布时间】:2011-01-03 07:56:18
【问题描述】:

我有一个关于 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, which would wrongly etc.”我也试过了

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

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

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

谢谢!

【问题讨论】:

    标签: ruby-on-rails form-for nested-resources


    【解决方案1】:

    您不需要在表单中做特殊的事情。您只需在显示操作中正确构建评论:

    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 页面。

    【讨论】:

    • 我必须使用form_for([@article, @new_comment]) 格式。我认为这是因为我显示的是comments#new 的视图,而不是article#new_comment。我认为article#new_comment Rails 足够聪明,可以计算出评论对象嵌套在什么中,这样您就不必指定它?
    【解决方案2】:

    确保在控制器中创建了两个对象:@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 助手。
    • 只设置动作嵌套的父对象。如果您只是部分嵌套了资源(例如,根据示例),那么设置父对象将导致 form_for 失败(刚刚用 rails 5.1 再次确认)
    【解决方案3】:

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

    有很多关于嵌套资源的讨论,例如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
    

    抱歉,答案很长。我认为没有多少人知道其中的微妙之处。

    【讨论】:

    • 它可以工作,但是我不得不像 jamuraa 说的那样修改控制器。
    • Jam 的方式可行,但您最终可能会得到您可能不知道的额外路线。最好是明确的。
    • 我有嵌套资源,@result 在@course 中。虽然,[@result, @course] 有效,但 form_for(@result, url: { action: "create" }) 也有效。这只需要最后一个模型名和方法名。
    • @cdunn2001 请你解释一下为什么我们必须在这里提到“@article”,这是什么意思?下面的语法是做什么的? :
    • Travis / @cdunn2001 做对了。使用没有重复的嵌套路由时,不要同时设置父级和资源,否则会认为所有操作都是嵌套的。同样,如果您嵌套了所有内容,则始终设置 AT.parent。此外,如果您有一个带有部分嵌套路由的取消按钮的通用表单,则使用如下路径,以便您设置的任何一个都可以使用(注意 child 的复数形式):
    猜你喜欢
    • 2011-01-16
    • 2012-07-08
    • 2016-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多