在进入细节之前,我已经通读了这些帖子,试图找到没有成功的解决方案:one,two,three
话虽这么说:我正在[新建并]建立一个ecomm网站来出售二手服装,鞋子和装饰品。
我的结构只有一个Product
模型以及关联的控制器和表。每个“产品”都有三个不同的主要类别之一,这就是我用来区分和创建3个不同URL的方式。
我的路线如下:
Rails.application.routes.draw do
root to: 'pages#home'
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
get 'shoes', to: 'products#shoes'
get 'shoes/:id', to: 'products#show'
get 'home', to: 'products#home'
get 'home/:id', to: 'products#show'
get 'products/new', to: 'products#new'
post 'products', to: 'products#create'
end
我的products_controller
如下:
class ProductsController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def shoes
@all_shoe_products = @all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def clothing
@all_clothing_products = @all_products.where(main_category: MainCategory.find_by_name("clothes").id)
end
def home
@all_home_products = @all_products.where(main_category: MainCategory.find_by_name("housewares").id)
end
def show
end
def new
@new_product = Product.new
end
private
def set_one_product
@product = Product.find(params[:id])
end
def set_all_products
@all_products = Product.all
end
end
当编写<%= link_to clothing_path(product) %>
(“产品”是.each
循环中的占位符)时,我得到一个路径:root/clothing.[:id]
而不是root/clothing/[:id]
我知道我犯了一个约定错误,尝试在同一个控制器中使用3个不同的URL可能是我错的地方。
注意:在地址栏中手动输入root/clothing/[:id]
确实可以正确返回产品。
答案 0 :(得分:2)
执行此操作时:
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show'
在您的routes.rb
中,它会创建以下路由(您可以通过在控制台中执行rake routes
看到这些路由):
clothing GET /clothing(.:format) products#clothing
GET /clothing/:id(.:format) products#show
如您所见,clothing_path
路由到/clothing
,而不是/clothing/:id
。因此,当您这样做时:
<%= link_to clothing_path(product) %>
rails将ID附加为.id
(这是您遇到的情况)。
答案 1 :(得分:1)
@jvillian在这里很好地解释了问题的原因,尽管我想提出一个轻微的重构作为解决方案。
这可能需要更多的工作,尽管使用shoes
,clothing
和home
的单独控制器并遵循RESTful设计可能会更好。这样一来,您便可以在路由文件中使用resources
。
例如,您的shoes_controller.rb
如下所示:
class ShoesController < ApplicationController
before_action :set_all_products
before_action :set_one_product, only: [:show]
def index
@all_shoe_products = @all_products.where(main_category_id: MainCategory.find_by_name("shoes").id)
end
def show
end
private
def set_one_product
@product = Product.find(params[:id])
end
def set_all_products
@all_products = Product.all
end
end
然后定义它们的路线为:
resources :shoes, only: [:index, :show]
对于其他资源,请遵循此模式,并且遵循良好的Rails约定,可以很好地隔离代码。
这将按照您的要求生成路线:
shoes GET /shoes(.:format) shoes#index
shoe GET /shoe/:id(.:format) shoes#show
这将解决您的问题,并为您提供一个设计精美的应用程序-也有机会推断出新控制器之间共享的一些代码,尽管这听起来像是一个后续任务:)
希望这会有所帮助-如果您有任何疑问或反馈,请告诉我。
答案 2 :(得分:0)
我找到了一个解决方案,尽管对我来说似乎有些逻辑上的奥秘,但它为何起作用。
在路线上.....
get 'clothing', to: 'products#clothing'
get 'clothing/:id', to: 'products#show', as: 'clothing/item'
在 index 页面中。...
<%= link_to clothing_item_path(product) do %>
这将产生正确的URL结构:root/clothing/[:id]
在测试时,我期望:root/clothing/item/[:id]
...尽管我更喜欢结果而不是我的期望
答案 3 :(得分:0)
我认为您想要的是参数化路由,例如:
get ':product_line', to: 'products#index'
get ':product_line/:id', to: 'products#show'
这将允许您创建任何数量的自定义产品线,而无需在控制器中定义新方法。假设您的product_line
模型上有一个Product
属性,则控制器将如下所示:
class ProductsController < ApplicationController
def index
@product_line = params[:product_line]
@products = Product.where(product_line: @product_line)
end
def show
@product_line = params[:product_line]
@product = Product.find(params[:id])
end
end
您的views / products / index.html.erb看起来像这样:
<p id="notice"><%= notice %></p>
<h1><%= @product_line %></h1>
<table>
<thead>
<tr>
<th>Description</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<% @products.each do |product| %>
<tr>
<td><%= product.description %></td>
<td><%= product.price %></td>
<td><%= link_to 'Show', "#{@product_line}/#{product.id}" %></td>
</tr>
<% end %>
</tbody>
</table>
请注意,link_to
不能再使用Rails帮助器方法来生成url。您必须自己做。
此方法的优点在于,用户可以在URL中键入任何产品系列。如果您拥有该产品系列(例如说“ sporting_goods”),请继续进行显示。如果没有,请渲染页面以感谢他们的关注,并记录某人要求该产品线的事实,以便您在扩展产品范围时评估自己的兴趣。
另外,它是RESTful的!是的!
答案 4 :(得分:0)
Rails解决此问题的方法是通过creating a nested resource:
resources :categories do
resources :products, shallow: true
end
这会嵌套收集路线,以便您获得GET /categories/:category_id/products
。
虽然这可能不像您的虚荣路线那么短,但它的用途更加广泛,因为它可以让您展示任何潜在类别的产品,而不会膨胀您的代码库。
您将按以下方式设置控制器:
class ProductsController < ApplicationController
before_action :set_category, only: [:new, :index, :create]
# GET /categories/:category_id/products
def index
@products = @category.products
end
# GET /categories/:category_id/products/new
def new
@product = @category.products.new
end
# POST /categories/:category_id/products
def new
@product = @category.products.new(product_params)
# ...
end
# ...
private
def set_category
@category = MainCategory.includes(:products)
.find_by!('id = :x OR name = :x', x: params[:id])
end
end
您可以使用category_products_path
命名路径助手来链接到任何类别的产品:
link_to "#{@category.name} products", category_products_path(category: @category)
您也可以使用polymorphic path helpers:
link_to "#{@category.name} products", [@category, :products]
form_for [@category, @product]
redirect_to [@category, :products]
如果要将未嵌套的GET /products
和嵌套的GET /categories/:category_id/products
路由到不同的控制器,一个巧妙的技巧是使用模块选项:
resources :products
resources :categories do
resources :products, only: [:new, :index, :create], module: :categories
end
这会将嵌套的路由路由到Categories::ProductsController
。