Ruby-on-rails Rails 4 [最佳实践] 嵌套资源和浅层:true

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21868544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:22:22  来源:igfitidea点击:

Rails 4 [Best practices] Nested resources and shallow: true

ruby-on-railsapirestruby-on-rails-4nested-routes

提问by Erowlin

The following post is based on Rails 4.

以下帖子基于 Rails 4。

I'm actually looking for a good best-practices about the multiple nested resources (more than 1), and the option shallow: true.

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

First in my routes, there was this :

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

resources :projects do 
  resources :collections
end

The routes associated are :

相关的路线是:

    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

I read in the documentation about the limitation of nested resources :

我阅读了有关嵌套资源限制的文档:

Resources should never be nested more than 1 level deep.

资源的嵌套深度不应超过 1 级。

Source : http://guides.rubyonrails.org/routing.html#limits-to-nestingOk. Then, like the documentation said, I'm gonna use "shallow" in my routes.

来源:http: //guides.rubyonrails.org/routing.html#limits-to-nesting好的。然后,就像文档所说的那样,我将在我的路线中使用“浅”。

shallow do
  resources :projects do 
    resources :collections
  end
end

The routes associated are :

相关的路线是:

   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

The major difference I see is the "show" of collections, this one :

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

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

So if I I'm right, the link for the show action for a collection is :

因此,如果我是对的,则集合的显示操作的链接是:

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

and should return something like this : "http://example.com/collections/1"

并且应该返回如下内容:“ http://example.com/collections/1

BUT ! 2 things :

但 !2件事:

  • This is not working. I'm getting instead "http://example.com/projects/1". WTF ?
  • Even if it was working, it's actually pretty bad because I loose the REST basic that say "Collection is child of project, then the url should be "localhost/project/1/collections/1"
  • 这是行不通的。我得到的是“ http://example.com/projects/1”。跆拳道?
  • 即使它正在工作,它实际上也很糟糕,因为我失去了 REST 基础,上面说“集合是项目的子项,那么 url 应该是”localhost/project/1/collections/1”

I don't understand what is the interest of shallow if it's to loose the big advantage of Rest actions. What's the interest ? And what is the interest to loose the "Show" action ? I already posted this to SO, but the only comment i got is "It's something normal". WTF? In what this is a normal behavior to "remove" an action from the rest API ?

如果要失去 Rest 动作的巨大优势,我不明白浅层的兴趣是什么。有什么兴趣?放松“表演”行动的兴趣是什么?我已经将此发布到 SO,但我得到的唯一评论是“这是正常的”。跆拳道?从其余 API 中“删除”操作的正常行为是什么?

I reproduced the problem on a neutral project, to be sure that I was not doing something wrong, and the same problem happened. So, yes, it may be convenient for the helpers to use shallow, but it's NOT AT ALL convenient for the rest, you loose all the interest of "one collection is nested to one project, so this is reflected in the URL".

我在一个中立的项目上重现了这个问题,以确保我没有做错什么,同样的问题也发生了。所以,是的,帮助者使用浅层可能很方便,但对于其他人来说一点也不方便,你失去了“一个集合嵌套到一个项目,所以这反映在 URL 中”的所有兴趣。

I don't know if there is another way to do this, it's true that shallow allow more flexibility about the helpers, but it's false that it's rest compliant. So, is there any chance to get the "helpers" working (it's pretty awesome to have "nested3_path(collection)" instead of "nested1_nested2_nested3([nested1.nested2.nested3, nested1.nested2, nested1])", and keeping the "url part" and keep having "nested1/123/nested2/456/nested3/789 ?

我不知道是否有另一种方法可以做到这一点,确实,浅层允许助手具有更大的灵活性,但它符合休息标准是错误的。那么,是否有机会让“助手”工作(拥有“nested3_path(collection)”而不是“nested1_nested2_nested3([nested1.nested2.nested3,nested1.nested2,nested1])”,并保持“ url 部分”并保持“nested1/123/nested2/456/nested3/789?

Thanks !

谢谢 !

采纳答案by Carlos Ramirez III

I don't believe that Rails offers any built-in way to have the URLs use the full hierarchy (e.g. /projects/1/collections/2) but also have the shortcut helpers (e.g. collection_pathinstead of project_collection_path).

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

If you really wanted to do this, you could roll out your own custom helper like the following:

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

def collection_path(collection)
  # every collection record should have a reference to its parent project
  project_collection_path(collection.project, collection)
end

But that would be quite cumbersome to manually do for each resource.

但是对于每个资源手动执行会非常麻烦。



I think the idea behind the use of shallowroutes is best summed up by the documentation:

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

One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource

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

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

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

So while this may not be REST-compliant (as you say), you aren't losing any information because each resource can be uniquely identified and you are able to walk back up the hierarchy assuming your associations are set up properly.

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

回答by aceofspades

Since there's an idfor a Collection, it's redundant to nest the route under the Project except for the indexand createactions.

由于有一个idfor a Collection,除了indexandcreate操作之外,在项目下嵌套路由是多余的。

There's a rule about URL's where there's only supposed to be one URL to GET (with 200) a given resource, if there are other URL's you should redirect to it. So you might have a route /projects/:id/collections/:collection_idthat redirects to /collections/:collection_id.

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

In your case, a Collection is tied to a Project, but that's not necessarily true for all relationships. Once you have the :collection_idyou don't need to reference the context of the Projectto access it.

在您的情况下,集合与项目相关联,但这不一定适用于所有关系。一旦你有了 ,:collection_id你就不需要引用 的上下文Project来访问它。

回答by Gary S. Weaver

Though it can complicate things if you only need this for some models, it might be good to check out Inherited Resources(IR). It supports resource nesting, polymorphic belongs to's, and can automatically generate the shorter path and url helper methods you are looking for. The reason you don't hear about IR much anymore is that its original author and some other developers have somewhat abandoned it because of the complications that arise when trying to extend your controllers. However, it still has a community, and we've tried to extend it a bit more and focus more on ease of controller extensions with Irie.

尽管如果您只需要某些模型使用它可能会使事情复杂化,但检查继承资源(IR)可能会很好。它支持资源嵌套,多态所属,并能自动生成你正在寻找的较短的路径和url辅助方法。您不再听说 IR 的原因是它的原始作者和其他一些开发人员在某种程度上放弃了它,因为在尝试扩展控制器时会出现复杂情况。但是,它仍然有一个社区,我们已经尝试对其进行更多扩展,并更多地关注Irie控制器扩展的易用性。

The "best practice" in Rails depends on who you talk to.

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

Rails has traditionally been aimed at mostly basic CRUD for (non-nested) resources. Yes, it allows retrieving and updating nested resources, but it is assumed that doesn't happen quite as often.

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

However, what has been emerging in the Rails community is the ActiveModel::Serializers/json-apiapproach. In this, usually not more than one level of nesting of resources occurs, and the nested resource is either a list of links or sideloaded small version of the child resources which you can then query on that resource to get more data. This has also been embraced by Ember/Ember Data.

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

There are also roarand a number of other projects that aim to implement something closer to their understanding of something close to Roy Fielding's original vision of REST.

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

I think it just depends on what your design is and what you need. If efficiency is a goal, then the additional time to develop to be explicit and nest more may pay off. We currently use AngularJSand Irie, for example. But, to each his own.

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

As as last note, be sure to avoid n+1 lookups through use of includes(...)(or similar) in your queries, otherwise all that nesting might bite you in performance.

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

回答by Richard Peck

Levels

级别

The notion you have to only use 1 level in your nested resources is only really applicable to the design of the system:

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

The corresponding route helper would be publisher_magazine_photo_url, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular article by Jamis Buck proposes a rule of thumb for good Rails design:

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

I believe Rails can still handle multiple levels, although it's not recommended from a usability perspective

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



Shallow

Although I've seen shallow used before, I've never used it myself

虽然之前见过浅浅的用过,但我自己没用过

From looking at the documentation, it seems shallow has a rather obscure purpose (I don't actually know why it's there). The problem is you aren't publicly passing the post_idparameter to your controller, leaving you to load the collectionwithout an important param

从查看文档来看,它似乎有一个相当模糊的目的(我实际上不知道它为什么在那里)。问题是你没有公开地将post_id参数传递给你的控制器,让你在collection没有重要参数的情况下加载

I would surmise (and this is just speculation), that the aim is to pass the param you require behind the scenes, so you're left with a public "shallow" route:

我猜测(这只是猜测),目的是在幕后传递您需要的参数,因此您只剩下一条公开的“浅层”路线:

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

I would imagine you'd get a URL helper like this:

我想你会得到一个这样的 URL 助手:

collection_path(project.id, collection.id)

This would come out as domain.com/collection/2

这将作为 domain.com/collection/2

回答by engineerDave

From this answerit seems shallow routes somewhat defy the convention of Rails, IMO.

从这个答案看来,浅层路线似乎在某种程度上违背了 Rails 的惯例,IMO。

I would think you wouldn't need the explicit path helper for a show route. The link_to helper should be able to infer it from the object's to_param method.

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

#your helper becomes 
link_to "show", collection

If you use the helper your way as you have above you probably need to pass the nested ID of the parent resource to the helper too.

如果您像上面那样使用帮助程序,您可能也需要将父资源的嵌套 ID 传递给帮助程序。

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