【问题标题】:Routing nested resources in Rails 3在 Rails 3 中路由嵌套资源
【发布时间】:2011-02-17 04:26:07
【问题描述】:

我觉得嵌套路由有一个很常见的情况,看起来像这样(在某种伪符号中):

'/:username/photos' => Show photos for User.find_by_username
'/photos' => Show photos for User.all

简而言之:我有用户。他们有照片。我希望能够在他们的页面上展示他们的照片。我还希望能够显示所有照片,无论用户如何。我想让我的路由保持 RESTful 并使用内置的 resource 方法感觉是正确的方法。


选项 1 这样做是让 PhotosController#index 使用条件来检查给出了哪些参数并获取照片列表并设置视图(对于用户的照片与所有照片不同) )。路由它甚至很容易:

resources :photos, :only => [:index]
scope ':/username' do
  resources :photos
end

轰隆隆。 似乎 Rails 就是为此设置的。然而,在路线之后,事情变得更加复杂。 PhotosController#index 操作中的条件返回变得越来越臃肿,并且正在做大量的委托。随着应用程序的增长以及我想展示照片的方式的数量也在增长,它只会变得更糟。

选项 2 可能是使用 User::PhotosController 来处理用户照片,并使用 PhotosController 来处理显示所有照片。

resources :photos, :only => [:index]
namespace :user, :path => '/:username' do
  resources :photos
end

生成以下路由:

           photos GET    /photos(.:format)                    {:action=>"index", :controller=>"photos"}
      user_photos GET    /:username/photos(.:format)          {:action=>"index", :controller=>"user/photos"}
                  POST   /:username/photos(.:format)          {:action=>"create", :controller=>"user/photos"}
   new_user_photo GET    /:username/photos/new(.:format)      {:action=>"new", :controller=>"user/photos"}
  edit_user_photo GET    /:username/photos/:id/edit(.:format) {:action=>"edit", :controller=>"user/photos"}
       user_photo GET    /:username/photos/:id(.:format)      {:action=>"show", :controller=>"user/photos"}
                  PUT    /:username/photos/:id(.:format)      {:action=>"update", :controller=>"user/photos"}
                  DELETE /:username/photos/:id(.:format)      {:action=>"destroy", :controller=>"user/photos"}

我认为这很好用,但一切都在一个用户模块下,当我将它与其他东西集成时,我觉得这最终可能会导致问题。

问题

  • 有人有类似的经验吗?
  • 谁能分享更好的处理方法?
  • 这些选项中的任何一个都需要考虑其他优点和缺点吗?

更新:我继续实施选项 2,因为它让 Rails 的逻辑工作而不是覆盖它感觉更清晰。到目前为止一切进展顺利,但我还需要将我的命名空间重命名为 :users 并添加一个 :as => :user 以防止它与我的 User 模型发生冲突。我还覆盖了User 模型上的to_param 方法以返回用户名。路径助手仍然以这种方式工作。

我仍然希望得到有关此方法的反馈。我是按预期的方式做事,还是滥用了这个功能?

【问题讨论】:

  • 当然,你可以问任何你想要的,但是你要求 RESTful 路由和明确要求像 /:username/photos 这样肯定不是 RESTful 的路由有点自相矛盾。我认为这个问题即使对于像 /users/:user_id/photos 这样的真正 RESTful 路由也是有效的,如果找到了针对这种情况的解决方案,而不是另一个问题,如何调整路由以映射到你需要的方式(如果你真的需要它,我认为这不是一个好主意,whatif some :username is photos ;-) 那么路线等存在冲突...)
  • @gom RESTful 与实际的 URL 段无关。它与利用 HTTP 请求动词(GET、POST、PUT、DELETE)来确定适当的操作有关。至于虚 URL(如/:username),这很常见。虽然它在这里被争论得令人作呕,但这并不是导致问题的部分。您可以轻松验证用户名不在保留名称列表中,并且由于我的路由设置正确,它不能覆盖我的任何其他路由。

标签: ruby-on-rails ruby ruby-on-rails-3 routing


【解决方案1】:

您是否考虑过在这种情况下使用浅嵌套路由?

浅层路由嵌套 有时,嵌套资源会产生繁琐的 URL。解决这个问题 是使用浅层路由嵌套:

resources :products, :shallow => true do
  resources :reviews
end

这将能够识别以下路线:

/products/1 => product_path(1)
/products/1/reviews => product_reviews_index_path(1)
/reviews/2 => reviews_path(2)

【讨论】:

  • 这不符合将用户名作为第一个 URL 段的路由约束,但没关系,因为我可以解决这个问题。但是,这并不能真正帮助回答我关于如何以干净的方式有条件地处理操作的问题。
  • 用户名是一个危险的第一个 url 段......它通常是查询字符串的一部分,但我想你可以检查以确保你没有任何名为 photos 的用户等。就条件而言去吧,如果它们不能轻易地用查询字符串表示,我会为它们添加单独的 url。这会将您臃肿的条件转移到路由逻辑。它必须去一个地方或另一个地方。
  • 感谢您的警告,但多年来我一直在非 Rails 应用程序中使用它们,但没有出现问题。不做虚荣 URL 是不可接受的;甚至 Facebook 也屈服于这种需求。
【解决方案2】:

执行此操作的最佳方法取决于应用程序,但在我的情况下肯定是选项 B。使用命名空间路由,我可以使用模块以非常干净的方式将不同的关注点分离到不同的控制器中。我还使用命名空间特定的控制器向特定命名空间中的所有控制器添加共享功能(例如,添加一个 before_filter 来检查命名空间中所有资源的身份验证和权限)。

【讨论】:

  • 如果您能提供一个这样的代码示例,那就太好了! (即使它只是复制和粘贴上面的选项并添加一些示例控制器代码)
【解决方案3】:

我在我的一个应用程序中做了类似的事情。你在正确的轨道上。我所做的是声明嵌套资源,并使用 Rails 3 中 Active Record 的灵活的基于 arel 的语法构建查询。在您的情况下,它可能看起来像这样:

# config/routes.rb
resources :photos, :only => :index
resources :users do
  resources :photos
end

# app/controllers/photos_controller.rb
def index
  @photos = Photo.scoped
  @photos = @photos.by_user(params[:user_id]) if params[:user_id]
  # ...
end

【讨论】:

  • 这与我上面描述的方式不同(例如'/username/photos')。切换视图怎么样?
  • 好吧,如果你真的希望他们的用户名出现在 URL 中,你可以在 User 模型上覆盖 to_param。如果路径以用户名开头(而不是/users/:username)也很重要,请四处搜索,因为该问题已经在 SO 上被问过多次。就我个人而言,我认为这不是一个好方法,因为它会让您与未来的控制器发生潜在的命名冲突。在视图中做一些不同的事情很容易——你可以根据params[:user_id]的存在编写条件逻辑。
  • 覆盖to_param 似乎是一个不错的选择,可以让路径助手在不显式传递用户名的情况下工作。不过,除此之外,我认为我喜欢“选项 2”提供的清晰性; Rails 已经知道以不同的方式加载照片,并且每次都加载正确的视图。关于虚 url 上的命名冲突,我过去做过,没有问题。只要你把它放在最后并限制有效用户的路线,它就可以很好地工作。在这种情况下,我也有“/photos/*”来防止野 URL 被路由为“/username/foobar”。
  • Jimmy:我认为这个问题被问到了,只是因为“PhotosController#index 操作中的条件返回变得越来越臃肿,并且正在做大量的委托。”所以是的,编写条件逻辑很容易,但是臃肿是goibg。 coreyward:“Rails 已经知道以不同方式加载照片”到底是什么意思。你还是需要自己做,不是吗?
【解决方案4】:

您可以定义一个单独的路径来为一个用户获取照片,如下所示:

get '(:username)/photos', :to => 'photos#index'

但我建议只使用 Jimmy 在上面发布的嵌套资源,因为这是最灵活的解决方案。

【讨论】:

  • 从 Jimmy Cuadra 出发的路线不符合我的需要。我看不出它比我发布的任何一个选项都更灵活。您建议的路线只是定义了我在选项 1 中已有的众多路线之一。
【解决方案5】:
Example::Application.routes.draw do
  resources :accounts, :path => '' do
    resources :projects, :path => '', :except => [:index]
  end
end

示例来自: http://jasoncodes.com/posts/rails-3-nested-resource-slugs

刚刚在我当前的项目中应用了它。

【讨论】:

    猜你喜欢
    • 2011-05-23
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多