Rails 4 [最佳实践] 嵌套资源和浅层:true
Posted
技术标签:
【中文标题】Rails 4 [最佳实践] 嵌套资源和浅层:true【英文标题】:Rails 4 [Best practices] Nested resources and shallow: true 【发布时间】:2014-03-19 01:53:02 【问题描述】:以下帖子基于 Rails 4。
我目前正在寻找关于多个嵌套资源(超过 1 个)的最佳实践,以及选项 shallow: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
我在documentation 中看到关于嵌套资源的限制:
资源的嵌套深度不得超过 1 级。
好的。然后,就像文档说的那样,我将在我的路线中使用“浅”。
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
我看到的主要区别是集合的“展示”,这个特定的:
collection GET /collections/:id(.:format) collections#show
所以如果我是正确的,集合的显示操作的链接是:
<%= link_to 'Show", collection_path(collection)%>
并且应该返回如下内容:“http://example.com/collections/1”
但是!两件事:
这不起作用。我得到的是“http://example.com/projects/1”。 即使它工作正常,但实际上 IMO 很糟糕,因为我松散了 REST 基本原则,即“集合是项目的子项,那么 url 应该是“localhost/project/1/collections/1”如果我失去了休息动作的巨大优势,我不明白浅薄的好处是什么。放开“表演”动作又有什么好处呢?我已经将此发布到 SO,但我得到的唯一评论是“这很正常”。我不认为这是从其余 API 中“删除”操作的正常行为?
是的,帮助者使用 shallow 可能很方便,但对其他人来说一点也不方便,你失去了“一个集合嵌套到一个项目,所以这反映在 URL 中”的所有兴趣.
我不知道是否还有其他方法可以做到这一点,shallow 确实允许帮助器具有更大的灵活性,但它符合 REST 是错误的。那么,有没有机会让“助手”工作(使用“nested3_path(collection)”而不是“nested1_nested2_nested3([nested1.nested2.nested3,nested1.nested2,nested1])”,并保持“网址部分“nested1/123/nested2/456/nested3/789”?
【问题讨论】:
你试过重启服务器让路由生效吗?根据文档resources :posts, shallow: true do resources :comments end
将产生resources :posts do resources :comments, except: [:show, :edit, :update, :destroy] end resources :comments, only: [:show, :edit, :update, :destroy]
这听起来像你在做
确实需要重启服务器才能让路由生效。
【参考方案1】:
From this answer 似乎浅层路线在某种程度上违反了 IMO 的 Rails 惯例。
我认为您不需要显示路径的显式路径助手。 link_to 助手应该能够从对象的 to_param 方法中推断出它。
#your helper becomes
link_to "show", collection
如果您按照上述方式使用助手,您可能还需要将父资源的嵌套 ID 传递给助手。
link_to "show", collection_path([project, collection])
【讨论】:
【参考方案2】:虽然如果您仅对某些型号需要此功能会使事情复杂化,但最好查看Inherited Resources (IR)。它支持资源嵌套、多态所属,并能自动生成你要找的短路径和url辅助方法。您不再听说 IR 的原因是它的原作者和其他一些开发人员已经放弃了它,因为尝试扩展控制器时会出现复杂情况。但是,它仍然有一个社区,我们已经尝试对其进行更多扩展,并更多地关注Irie 的控制器扩展的易用性。
Rails 中的“最佳实践”取决于您与谁交谈。
Rails 传统上主要针对(非嵌套)资源的基本 CRUD。是的,它允许检索和更新嵌套资源,但假设这种情况不会经常发生。
然而,Rails 社区中出现的是ActiveModel::Serializers/json-api 方法。在这种情况下,通常不会发生超过一级的资源嵌套,嵌套资源是链接列表或子资源的侧载小版本,然后您可以在该资源上查询以获取更多数据。 Ember/Ember Data也接受了这一点。
还有roar 和许多其他项目旨在实现更接近他们对接近 Roy Fielding 最初的 REST 愿景的理解的东西。
我认为这取决于您的设计是什么以及您需要什么。如果效率是一个目标,那么额外的时间来明确和嵌套更多可能会得到回报。例如,我们目前使用AngularJS 和Irie。但是,各有各的。
如最后一点,请务必通过在查询中使用 includes(...)
(或类似名称)来避免 n+1 查找,否则所有嵌套可能会影响性能。
【讨论】:
【参考方案3】:因为Collection
有一个id
,所以除了index
和create
操作之外,在项目下嵌套路由是多余的。
有一个关于 URL 的规则,其中应该只有一个 URL 来获取(200)给定的资源,如果有其他 URL,你应该重定向到它。所以你可能有一个路由/projects/:id/collections/:collection_id
重定向到/collections/:collection_id
。
在您的情况下,集合与项目相关联,但这并不一定适用于所有关系。拥有:collection_id
后,您无需引用Project
的上下文即可访问它。
【讨论】:
是的!谢谢!我一直在谷歌搜索寻找浅层路线目的的基本解释!文档中的假设是:您已经知道这一点。从这里的其他一些答案来看,很多其他人也不知道!您的意思是,例如,如果您通过 :id 编辑评论,则将其嵌套在项目下是没有意义的,因为评论 :id 独立于项目 id。浅层只是清理路线以反映这一点。【参考方案4】:我不相信 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(如您所说),但您不会丢失任何信息,因为每个资源都可以被唯一标识,并且假设您的关联设置正确,您可以返回层次结构。
【讨论】:
虽然它没有内置在 Rails 中,但 Inherited Resources (IR) 有嵌套和帮助器可能会有所帮助。如果只是在少数情况下使用 IR,那么明确而不使用 IR 可能会更好,并且它还可以限制或复杂化控制器实现/调试,具体取决于您的需要。【参考方案5】:级别
您必须在嵌套资源中仅使用 1 级的概念仅适用于系统设计:
对应的路由助手是 publisher_magazine_photo_url, 要求您在所有三个级别上指定对象。的确,这 情况令人困惑,以至于 Jamis Buck 的一篇热门文章 提出了一个好的 Rails 设计的经验法则:
我相信 Rails 仍然可以处理多个级别,尽管从可用性的角度不建议这样做
浅
虽然之前见过sallow用过,但我自己没用过
从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
【讨论】:
是的,您在路线上是正确的,但这对我的问题没有多大帮助 =/以上是关于Rails 4 [最佳实践] 嵌套资源和浅层:true的主要内容,如果未能解决你的问题,请参考以下文章
为浅层路由寻求最佳 RestKit/CoreData 映射和 JSON 结构的建议