为什么我的Rails URL路由使用点而不是斜杠来呈现URL?

时间:2018-12-14 08:46:20

标签: ruby-on-rails controller routes

在进入细节之前,我已经通读了这些帖子,试图找到没有成功的解决方案:onetwothree

话虽这么说:我正在[新建并]建立一个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]确实可以正确返回产品。

5 个答案:

答案 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在这里很好地解释了问题的原因,尽管我想提出一个轻微的重构作为解决方案。

这可能需要更多的工作,尽管使用shoesclothinghome的单独控制器并遵循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