Rails 不区分嵌套在相同命名空间中的两个资源

Posted

技术标签:

【中文标题】Rails 不区分嵌套在相同命名空间中的两个资源【英文标题】:Rails not differentiating between two resources nested in the same namespaces 【发布时间】:2021-09-20 05:13:13 【问题描述】:

我有两种类型的产品嵌套在相同的类别下。我设置的路线是

resources :categories, path: '/', only: [:show] do
  resources :subcategories, path: '/', only: [:show] do
    resources :amazon_products, path: '/', only: [:show]
    resources :other_products, path: '/', only: [:show]
  end
end

我之前使用这个链接访问过的

<%= link_to "View Product Page", [product.collection, product.category, product.subcategory, product], class: 'product__link' %>

friendly_id 完成后导致类似此网址的内容

/cleansers/face-wash-and-cleansers/blemish-remedy-acne-treatment-gelee-cleanser

问题是该链接仅适用于 amazon_products,我不确定如何区分这两者。我认为问题出在我引用路径的方式上,因为当我在控制台中输入 rails 路由时,我可以看到两条不同的路径,就像这样

category_subcategory_amazon_product
GET    :category_id/:subcategory_id/:id(.:format)
amazon_products#show

category_subcategory_other_product
GET    /:collection_id/:category_id/:subcategory_id/:id(.:format)
other_products#show

我尝试使用链接专门引用其他产品路径

category_subcategory_other_product_path(product.category, product.subcategory, product)

但它给了我一个 ActiveRecord::RecordNotFound 因为它仍然在错误的控制器中查找

app/controllers/amazon_products_controller.rb:5:in `show'

如何让 Rails 区分这两种资源?

【问题讨论】:

【参考方案1】:

归根结底,Rails 路由是相对简单的模式,可以匹配 URI 并将请求分派到正确的控制器/方法。

如果您以基本形式指定了嵌套路由:

resources :categories, only: [:show] do
  resources :subcategories, only: [:show] do
    resources :amazon_products, only: [:show]
    resources :other_products, only: [:show]
  end
end

产品级别的结果 URI 模式如下所示:

/categories/<id>/subcategories/<id>/amazon_products/<id>
/categories/<id>/subcategories/<id>/other_products/<id>

诚然冗长,但明显不同的模式。

问题是您在资源上使用path: '/' 来删除有关资源路由的所有独特内容。因此,您的产品级路由模式是:

category_subcategory_amazon_product: /<id>/<id>/<id>
category_subcategory_other_product: /<id>/<id>/<id>

由于这两种模式是相同的,Rails 回退到匹配第一个定义的历史悠久的做法。您可以通过交换:other_products 为自己证明这一点,所以它是第一个;然后它的控制器将始终被调用。

注意:使用friendly-id 并不重要——它只是改变了用户对ID 的看法,而不是基本的路由模式。

解决方案是简单地重新引入一些独特性,至少在产品级别。

resources :categories, path: '/', only: [:show] do
  resources :subcategories, path: '/', only: [:show] do
    resources :amazon_products, path: 'amazon', only: [:show]
    resources :other_products, path: 'other', only: [:show]
  end
end

结果将是 Rails 可以实际区分的路由模式:

category_subcategory_amazon_product: /<id>/<id>/amazon/<id>
category_subcategory_other_product: /<id>/<id>/other/<id>

【讨论】:

以上是关于Rails 不区分嵌套在相同命名空间中的两个资源的主要内容,如果未能解决你的问题,请参考以下文章

Rails 命名空间与嵌套资源

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

Rails 路由:如何重命名(嵌套)资源块中的 params-Hash 键?

命名空间模型中的 Rails 关联

Rails form_for 具有相同属性的嵌套和关联资源已填充文本

Rails 和 RSpec:在不同的命名空间(模块)中测试具有相同名称的控制器