Rails 命名空间与嵌套资源

Posted

技术标签:

【中文标题】Rails 命名空间与嵌套资源【英文标题】:Rails Namespace vs. Nested Resource 【发布时间】:2011-07-06 19:28:35 【问题描述】:

假设我的应用有两个模型,Foo 和 Bar。

Foo 可选belongs_to Bar。

现在我可以查看单个 Foo,或搜索特定的 Foo,而 FoosController 会处理所有这些。我的网址是这样的: foos/1foos/new

有时我想看看酒吧。 BarsController 处理它,我像这样处理它: bars/1bars/1/edit

如果我正在查看一个 Bar,我可能想浏览属于该 Bar 的所有 Foo。所以,我想用bars/1/foos/ 来看看那些Foos。

这对于嵌套资源来说非常简单,看起来像这样:

resources :foo
resources :bar do
  resources :foo
end

但是,作为 Bar 一部分的 Foo 有点特殊,与常规 Foo 不同。因此,例如,如果我加载 foos/1bars/1/foos/1,我会查看相同的 Foo,但在每种情况下我都关注不同的信息。

所以我一直在考虑让 BarFoos 控制器在 Bar 的上下文中处理 Foos。但是,如果我将 BarFoos 嵌套在 Bar 下,那么我的助手将类似于 bar_bar_foos_pathnew_bar_bar_foo_path。这似乎是多余的。

所以,现在我正在考虑命名空间,这是我以前从未研究过的。我在 Rails 指南中看到我可以定义:

namespace "bar" do
  resources :foos
end

如果我这样做,我可以在app/bar/ 下创建第二个FoosController,并且 FoosController 可以使用像 bar_foo_path(:id) 这样的好帮手而不是 bar_bar_foo_path(:id) 来处理 Bar 内的 Foos。

但如果我这样做,我的BarsController 会发生什么?如果我有namespace "bar" 而不是resources :bars,请求如何路由到BarsController

最后,我需要在我的辅助 FoosController 中做些什么特别的事情来确保与*** FoosController 没有名称冲突吗?我意识到路由说“命名空间”,但是其余的 ruby​​ 代码如何知道 app/bar/foos_controllerapp/foos_controller 不是同一个类?

谢谢!

【问题讨论】:

【参考方案1】:

这种方法有缺点。

如果你声明一个常量,例如。 CONST_NAME,在嵌套资源 foos 中,rails 将抛出“未初始化的常量 ::Foo::CONST_NAME”异常,因为它的范围算法。

为避免此类行为,请使用:

resources :foos
resources :bars do
  scope :module => "bar" do
    resources :foos #, :controller => 'bar/foos' no need to use this now because route will be searched there by default
  end
end

现在您在使用时不会出现异常:

Foo::CONST_NAME

Bar::Foo::CONST_NAME

【讨论】:

【参考方案2】:

我认为你想要实现的是:

    酒吧有很多 Foos 查看属于 Bar 的 Foos 查看所有 Foos,无论其父母是谁。

您可以通过以下方式实现: 路线.rb:

resources :foos
resources :bars do
  resources :foos, :controller => 'bars/foos'
end

您最终得到的路线助手是:

bars_path foos_path bars_foos_path 等,等等,其余的 'rake routes' =)

本质上,你最终会得到:

app/BarsController(rails g 控制器栏) app/FoosController (rails g 控制器 foos) app/bars/FoosController(rails g 控制器 bar/foos)

在 FoosController 中,您可以像往常一样访问 foos:

@foos = Foos.all

在 bar/FoosController 中,您可以使用以下命令访问 bar 的 foo:

@foos = @bar.foos

bar 可以通过以下方式在 bar/foos 控制器中预检索:

before_filter :get_client

private
def get_client
  @bar = Bar.find(params[:bar_id])
end

希望这会有所帮助。 =)

编辑: 至于命名空间路由,当我从子路径中检索到一些资源时,我亲自使用过它们。例如,如果我的网站有一个管理部分,那么我可能有以下内容:

routes.rb:

namespace :admin do
  resources :foos
end

然后我创建了我的控制器:

rails g controller admin/foos

这设置了我的 foos 资源,这样我就可以在“我的站点 url”/admin/foos 中访问它,还可以获取 admin_foos_path 等帮助程序。

【讨论】:

你对我的场景的描述完全正确,而且你的回答很清楚,谢谢!我知道你的答案是如何工作的,太棒了!只是为了跟进一个细节,您会建议在这种情况下嵌套在命名空间上吗?你能提供任何你经历过的任何优点/缺点吗? 在您的情况下,因为 bar 和 foo 都是资源,而 foo 属于 bar,所以使用嵌套资源非常有意义。扩展上面的命名空间解释:如果您处于针对不同上下文处理不同行为的情况,即如果我是管理员,我做 A,否则我做 B,并且逻辑是除了最简单或分散在各处,然后我将考虑命名空间并添加一个控制器以允许我在该特定上下文中工作。在这种情况下,'admin' 表示上下文而不是资源。 太好了,很有道理!

以上是关于Rails 命名空间与嵌套资源的主要内容,如果未能解决你的问题,请参考以下文章

如何避免Rails 5嵌套资源命名空间路由中的双下划线

Rails 5,具有命名空间资源的Pundit

Rails:form_for 命名空间资源

嵌套命名空间路由到错误的控制器

Rails引擎:命名空间多态URL

Rails 方式 - 命名空间