Rails 4 嵌套资源和浅:真

以下post基于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 

我看到的主要区别是collections品的“展示”,这个:

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

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

  

并应该返回这样的内容:“ http://example.com/collections/1 ”

但是! 2件事:

  • 这不起作用。 我正在改为“ http://example.com/projects/1 ”。 WTF?
  • 即使它正在工作,它实际上也很糟糕,因为我放弃了REST基础,说“集合是项目的子项,那么url应该是”localhost / project / 1 / collections / 1“

如果松开Rest动作的巨大优势,我不明白浅层的兴趣是什么。 有什么兴趣? 放松“表演”行动的兴趣是什么? 我已经把它发布到了SO,但我得到的唯一评论是“这是正常的”。 WTF? 这是从其他API“删除”操作的正常行为?

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

我不知道是否还有另一种方法可以做到这一点,浅薄的是允许帮助者更多的灵活性,但是它的rest是否合规是错误的。 那么,有没有机会让“助手”工作(拥有“nested3_path(collection)”而不是“nested1_nested2_nested3([nested1.nested2.nested3,nested1.nested2,nested1])”非常棒,并保持“ url part“并且一直有”nested1 / 123 / nested2 / 456 / nested3 / 789?

谢谢 !

我不相信Rails提供任何内置方式让URL使用完整的层次结构(例如/projects/1/collections/2 ),但也有快捷方式帮助程序(例如collection_path而不是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 

但是,对每个资源手动执行操作非常麻烦。


我认为使用shallow路线背后的想法最好由文档总结:

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

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

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

由于Collection有一个id ,因此除了indexcreate操作之外,在项目下嵌套路径是多余的。

有一个关于URL的规则,其中只有一个URL是GET(200)给定资源,如果有其他URL,你应该重定向到它。 所以你可能有一个route /projects/:id/collections/:collection_id重定向到/collections/:collection_id

在您的情况下,Collection与项目绑定,但对于所有关系而言并不一定如此。 获得:collection_id您无需引用Project的上下文来访问它。

水平

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

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

我相信Rails仍然可以处理多个级别,尽管从可用性角度来看不建议使用它


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

从文档看 ,它似乎浅薄有一个相当模糊的目的(我实际上不知道它为什么存在)。 问题是你没有公开将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

虽然如果你只需要对某些模型使用它会使事情复杂化,但最好检查一下inheritance资源 (IR)。 它支持资源嵌套,多态属于,并且可以自动生成您正在寻找的更短路径和url辅助方法。 您之所以没有听到IR的原因是它的原作者和其他一些开发人员因为尝试扩展控制器时出现的复杂性而有所放弃。 但是,它仍然有一个社区,我们已经尝试将其扩展一点,并且更多地关注与Irie的控制器扩展的简易性。

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

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

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

还有一些咆哮和许多其他项目旨在实现更接近他们对Roy Fielding最初的REST愿景的理解。

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

与最后一点一样,请务必通过在查询中使用includes(...) (或类似)来避免n + 1次查找,否则所有嵌套都可能会影响您的性能。

从这个答案来看,似乎浅薄的路线有点违背了Rails,IMO的惯例。

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

 #your helper becomes link_to "show", collection 

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

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