Charles Smith
Charles Smith

Reputation: 3289

Rails 5 Nested Resources Urls with Has-Many Through Association

I've created a small test app utilizing the Has-Many Through Association method but I can seem to get link_to to work with within my nested routes.

error message

No route matches {:action=>"show", :controller=>"categories", :location_id=>#<Category id: 1, name: "parties", created_at: "2017-11-01 17:40:25", updated_at: "2017-11-01 17:40:25">}, missing required keys: [:id]

location.rb

class Location < ApplicationRecord
  belongs_to :product
  belongs_to :category
end

category.rb

class Category < ApplicationRecord
  has_many :locations
  has_many :products, through: :locations
end

product.rb

class Product < ApplicationRecord
  has_many :locations
  has_many :categories, through: :locations
end

routes.rb

Rails.application.routes.draw do
  resources :locations do
    resources :categories do
      resources :products
    end
  end
  root :to => 'locations#index'
end

categories/index.html

...
<% @categories.each do |category| %>
   <tr>
     <td><%= category.name %></td>
     <td><%= link_to 'Show', location_category_path(category) %></td>
     <td><%= link_to 'Edit', edit_location_category_path(category) %></td>
     <td><%= link_to 'Destroy', location_categories_path(category), method: :delete, data: { confirm: 'Are you sure?' } %></td>
   </tr>
<% end %>
...

127.0.0.1:3000/rails/info/routes

location_category_products_path GET /locations/:location_id/categories/:category_id/products(.:format)  
products#index

POST    /locations/:location_id/categories/:category_id/products(.:format)  
products#create

new_location_category_product_path  GET /locations/:location_id/categories/:category_id/products/new(.:format)  
products#new

edit_location_category_product_path GET /locations/:location_id/categories/:category_id/products/:id/edit(.:format) 
products#edit

location_category_product_path  GET /locations/:location_id/categories/:category_id/products/:id(.:format)  
products#show

PATCH   /locations/:location_id/categories/:category_id/products/:id(.:format)  
products#update

PUT /locations/:location_id/categories/:category_id/products/:id(.:format)  
products#update

DELETE  /locations/:location_id/categories/:category_id/products/:id(.:format)  
products#destroy

location_categories_path    GET /locations/:location_id/categories(.:format)    
categories#index

POST    /locations/:location_id/categories(.:format)    
categories#create

new_location_category_path  GET /locations/:location_id/categories/new(.:format)    
categories#new

edit_location_category_path GET /locations/:location_id/categories/:id/edit(.:format)   
categories#edit

location_category_path  GET /locations/:location_id/categories/:id(.:format)    
categories#show

PATCH   /locations/:location_id/categories/:id(.:format)    
categories#update

PUT /locations/:location_id/categories/:id(.:format)    
categories#update

DELETE  /locations/:location_id/categories/:id(.:format)    
categories#destroy

locations_path  GET /locations(.:format)    
locations#index

POST    /locations(.:format)    
locations#create

new_location_path   GET /locations/new(.:format)    
locations#new

edit_location_path  GET /locations/:id/edit(.:format)   
locations#edit

location_path   GET /locations/:id(.:format)    
locations#show

PATCH   /locations/:id(.:format)    
locations#update

PUT /locations/:id(.:format)    
locations#update

DELETE  /locations/:id(.:format)    
locations#destroy

root_path   GET /   
locations#index

Upvotes: 0

Views: 455

Answers (3)

Cyzanfar
Cyzanfar

Reputation: 7136

For the route:

location_category_path  GET /locations/:location_id/categories/:id(.:format)    

You need several things, the location id object and category id object:

<!-- /locations/:location_id/categories/:id -->
<%= link_to 'Show', [{location_id}, {category_id}] %>

Upvotes: 1

Jay-Ar Polidario
Jay-Ar Polidario

Reputation: 6603

Just to add to Arslan' answer.

You'll probably need to set @location in your controller if you don't have yet.

app/controllers/categories_controller.rb

class CategoriesController < ApplicationController
  def index
    @location = # your code here
  end
end

app/views/categories/index.html.erb

<% @categories.each do |category| %>
   <tr>
     <td><%= category.name %></td>
     <td><%= link_to 'Show', location_category_path(@location.id, category.id) %></td>
     <td><%= link_to 'Edit', edit_location_category_path(@location.id, category.id) %></td>
     <td><%= link_to 'Destroy', location_categories_path(@location.id, category.id), method: :delete, data: { confirm: 'Are you sure?' } %></td>
   </tr>
<% end %>

For routes URL parameters, you can use either:

location_category_path(@location.id, category.id)

Which follows the order of your routes (which is :location_id then :id) when you do bundle exec rake routes, or you can also be specific like the following:

location_category_path(location_id: @location.id, id: category.id)

Upvotes: -1

Arslan Ali
Arslan Ali

Reputation: 17802

location_category_path  GET /locations/:location_id/categories/:id(.:format)    

As you can see here, the route: location_category_path required two things: :location_id & :id(the id of category you want to refer to).

In your case, you are only specifying one, and not the other. You also need to specify :location_id.

There is a shorter way as well to write this URL: [location_id, category_id]. Just write an array with both ids starting with location_id.

Upvotes: 1

Related Questions