Rails 4 [最佳实践]嵌套资源和浅层:真实

时间:2014-02-19 00:30:57

标签: ruby-on-rails api rest ruby-on-rails-4 nested-routes

以下帖子基于Rails 4.

我实际上正在寻找关于多个嵌套资源(超过1个)的良好最佳实践,并且选项浅:true。

首先在我的路线中,有这个:

resources :projects do 
  resources :collections
end

相关的路线是:

    project_collections GET    /projects/:project_id/collections(.:format)          collections#index
                        POST   /projects/:project_id/collections(.:format)          collections#create
 new_project_collection GET    /projects/:project_id/collections/new(.:format)      collections#new
edit_project_collection GET    /projects/:project_id/collections/:id/edit(.:format) collections#edit
     project_collection GET    /projects/:project_id/collections/:id(.:format)      collections#show
                        PATCH  /projects/:project_id/collections/:id(.:format)      collections#update
                        PUT    /projects/:project_id/collections/:id(.:format)      collections#update
                        DELETE /projects/:project_id/collections/:id(.:format)      collections#destroy
               projects GET    /projects(.:format)                                  projects#index
                        POST   /projects(.:format)                                  projects#create
            new_project GET    /projects/new(.:format)                              projects#new
           edit_project GET    /projects/:id/edit(.:format)                         projects#edit
                project GET    /projects/:id(.:format)                              projects#show
                        PATCH  /projects/:id(.:format)                              projects#update
                        PUT    /projects/:id(.:format)                              projects#update
                        DELETE /projects/:id(.:format)                              projects#destroy

我在文档中读到了嵌套资源的限制:

  

资源不应该嵌套超过1级。

来源:http://guides.rubyonrails.org/routing.html#limits-to-nesting 好。然后,就像文件说的那样,我会使用"浅"在我的路线。

shallow do
  resources :projects do 
    resources :collections
  end
end

相关的路线是:

   project_collections GET    /projects/:project_id/collections(.:format)     collections#index
                       POST   /projects/:project_id/collections(.:format)     collections#create
new_project_collection GET    /projects/:project_id/collections/new(.:format) collections#new
       edit_collection GET    /collections/:id/edit(.:format)                 collections#edit
            collection GET    /collections/:id(.:format)                      collections#show
                       PATCH  /collections/:id(.:format)                      collections#update
                       PUT    /collections/:id(.:format)                      collections#update
                       DELETE /collections/:id(.:format)                      collections#destroy
              projects GET    /projects(.:format)                             projects#index
                       POST   /projects(.:format)                             projects#create
           new_project GET    /projects/new(.:format)                         projects#new
          edit_project GET    /projects/:id/edit(.:format)                    projects#edit
               project GET    /projects/:id(.:format)                         projects#show
                       PATCH  /projects/:id(.:format)                         projects#update
                       PUT    /projects/:id(.:format)                         projects#update
                       DELETE /projects/:id(.:format)                         projects#destroy

我看到的主要区别是" show"收藏品,这一个:

collection GET    /collections/:id(.:format)                      collections#show

所以,如果我是对的,那么集合的show动作的链接是:

<%= link_to 'Show", collection_path(collection)%>

并且应该返回以下内容:&#34; http://example.com/collections/1&#34;

但是! 2件事:

  • 这不起作用。我得到了#34; http://example.com/projects/1&#34;。 WTF?
  • 即使它正在工作,它实际上也很糟糕,因为我松开了REST基础,说&#34; Collection是项目的子项,那么url应该是&#34; localhost / project / 1 /集合/ 1&#34;

如果忽略了Rest行动的巨大优势,我不明白浅薄的利益是什么。感兴趣的是什么?什么是放松&#34; Show&#34;行动?我已经把它发布到了SO,但我得到的唯一评论是&#34;它是正常的&#34;。 WTF?这是一个正常的行为,以及#34;删除&#34;其余API的操作?

我在一个中立项目上重现了这个问题,以确保我没有做错事,并且发生了同样的问题。所以,是的,帮助者使用浅层可能很方便,但对其余部分来说并不方便,你放弃了所有的兴趣&#34;一个集合嵌套到一个项目,所以这是反映在网址&#34;。

我不知道是否有另一种方法可以做到这一点,浅薄的是允许助手更灵活,但是它的休息是否合规是错误的。那么,有没有机会获得帮助&#34;工作(#34; nested3_path(集合)&#34;而不是&#34; nested1_nested2_nested3([nested1.nested2.nested3,nested1.nested2,nested1])&#34;,非常棒!保持&#34; url部分&#34;并继续使用&#34; nested1 / 123 / nested2 / 456 / nested3 / 789?

谢谢!

5 个答案:

答案 0 :(得分:20)

我不相信Rails提供任何内置方式让URL使用完整的层次结构(例如/projects/1/collections/2),但也有快捷方式助手(例如collection_path而不是{{1 }})。

如果你真的想这样做,你可以推出自己的自定义助手,如下所示:

project_collection_path

但是,为每个资源手动操作会非常麻烦。


我认为使用def collection_path(collection) # every collection record should have a reference to its parent project project_collection_path(collection.project, collection) end 路线背后的想法最好由文档总结:

  

避免深度嵌套的一种方法(如上所述)是生成   在父级下限定的集合操作,以便有意义   层次结构,但不嵌套成员操作。换一种说法,   仅构建具有最少信息量的路由   唯一标识资源

来源:http://guides.rubyonrails.org/routing.html#shallow-nesting

因此,虽然这可能不符合REST(正如您所说),但您不会丢失任何信息,因为每个资源都可以唯一标识,并且您可以在假设您的关联正确设置的情况下返回层次结构。

答案 1 :(得分:10)

由于id存在Collection,因此除了indexcreate操作外,在项目下嵌套路线是多余的。

有一个关于URL的规则,其中只有一个URL是GET(有200个)给定资源,如果还有其他URL,你应该重定向到它。因此,您可能会将路由/projects/:id/collections/:collection_id重定向到/collections/:collection_id

在您的情况下,Collection与项目绑定,但并非所有关系都是如此。获得:collection_id后,您无需引用Project的上下文来访问它。

答案 2 :(得分:2)

<强>水平

您必须在嵌套资源中仅使用1级的概念才真正适用于系统的设计:

  

相应的路线助手是publisher_magazine_photo_url,   要求您在所有三个级别指定对象。的确,这个   情况令人困惑,这是Jamis Buck的一篇热门文章   为好的Rails设计提出了一个经验法则:

我相信Rails仍然可以处理多个级别,尽管从可用性角度来看并不推荐


<强>浅

虽然我之前看过浅色,但我自己从未使用过它

documentation看,它似乎浅薄有一个相当模糊的目的(我实际上并不知道它为什么存在)。问题是您没有公开将post_id参数传递给您的控制器,让您加载collection而没有重要的参数

我猜测(这只是推测),目的是通过你在幕后所需的参数,所以你留下了一条公共的“浅层”路线:

#config/routes.rb
resources :projects do 
   resources :collections, shallow: true
end

我想你会得到一个像这样的URL帮助器:

collection_path(project.id, collection.id)

这将以domain.com/collection/2

的形式出现

答案 3 :(得分:2)

虽然如果你只为某些型号需要这样做会使事情复杂化,但最好还是查看Inherited Resources(IR)。它支持资源嵌套,多态属于,并且可以自动生成您正在寻找的较短路径和url辅助方法。您之所以不再听到IR的原因是它的原始作者和其他一些开发人员因为尝试扩展控制器时出现的复杂性而有所放弃。但是,它仍然有一个社区,我们已尝试将其扩展一点,并更加注重使用Irie轻松实现控制器扩展。

Rails中的“最佳实践”取决于您与谁交谈。

传统上,Rails主要针对(非嵌套)资源的基本CRUD。是的,它允许检索和更新嵌套资源,但是假设这种情况不常发生。

然而,Rails社区中出现的是ActiveModel::Serializers / json-api方法。在这种情况下,通常不会出现多于一级的资源嵌套,嵌套资源可以是链接列表,也可以是子资源的侧载小版本,然后您可以在该资源上查询以获取更多数据。 Ember / Ember Data也接受了这一点。

还有roar和许多其他项目旨在实现更接近他们对罗伊菲尔丁最初的REST愿景的理解的东西。

我认为这取决于您的设计是什么以及您需要什么。如果效率是一个目标,那么发展为明确和更多巢穴的额外时间可能会有所回报。我们目前使用AngularJSIrie。但是,对每一个他自己。

与最后一点一样,请确保在查询中使用includes(...)(或类似)来避免n + 1次查找,否则所有嵌套都可能会让您陷入困境。

答案 4 :(得分:1)

From this answer似乎浅薄的路线有点违背了Rails,IMO的惯例。

我认为你不需要显示路径的显式路径助手。 link_to帮助器应该能够从对象的to_param方法推断它。

#your helper becomes 
link_to "show", collection

如果您按照上面的方式使用帮助程序,则可能需要将父资源的嵌套ID传递给帮助程序。

link_to "show", collection_path([project, collection])