I'm actually having trouble finding the documentation for this so if you have a link handy that would be really appreciated too.
So I have:
resources :users do
resources :posts, only: [:index, :create, :show]
end
I wanted to access the index action of posts through a named route. I tried this: <%= link_to 'User Posts', user_posts_path %> but it said it was missing user_id. Any ideas?
When using the nested resource routes, you would need to provide the reference id of the parent resource. In your case resource user. You could do: user_posts_path(user). The route generated would be something like: /users/1/posts where 1 is the :user_id or if you would rather want a route like: /users/posts you should do:
resources :users do
collection do
resources :posts
end
end
Find full routing documentation here
it's asking for user_id because you're defining :users as a resource, change it for a namespace instead:
namespace :users do
resources :posts, only: [:index, :create, :show]
end
Related
I have a rails nested resource in my routing.
i.e
resources :users do
resources :accounts
end
resources :accounts
The listing operations of course will be:
GET /users
GET /users/:user_id/accounts
I want to get rid of the /users route but retain the /users/:id/accounts route.
Any idea how I can go about this? Thanks
Using except: [:index] not will restrict both routes. Thats a nonsenical claim that can easily be refuted by just running rails routes. None of the options for resources "trickle down" to nested calls.
resources :users, only: [] do
resources :accounts, only: :index
end
only: [] skips generation of all the "user" routes.
This will generate the routes:
Prefix Verb URI Pattern Controller#Action
user_accounts GET /users/:user_id/accounts(.:format) accounts#index
# ...
Note that the param key is :user_id and not :id. If you REALLY want to break the conventions you would need to do:
# don't do this - its stupid
scope '/users/:id', as: :user do
resources :accounts, only: :index
end
let set only: [] then rails routes will generate /users/:id/accounts as you want
resources :users, only: [] do
resources :accounts # , only: [:index] if you just only keep users/:id/accounts
end
# if you only want to get rid of GET /users
resources :users, except: [:index]
# if you mean you want to get rid all of /users routes (not just only GET /users) then comment above line
This is what scope is for
scope :users do
resources :accounts
end
Rails guides on routing
Or you can use these same two ways, use namespace with scope:
namespace :users do
scope ':user_id' do
resources :accounts
end
end
Use just only scope:
scope ':users/:user_id' do
resources :accounts
end
Our website is rails 4.2.8 and ruby 2.4.1, we have products to sell, so our routes like:
resources :products
resources :orders, only: [:new, :create, :destroy] do
collection do
get :success
get :fail
get :details
end
so orders url like: https://example.com/orders/new?params...
but we want add product name on url for tracking purpose, something like:
https://example.com/product_name/orders/new?params...
can I accomplish this without change route resources or controller position?
In short, No you cannot..
You have to define such nested routes in your routes.rb which will change the paths and so all dependencies needs to be changed..
To be honest,
https://example.com/product_name/orders/new?params this url doesn't make sense to me..
Ideally, correct me if I am wrong, your order must be having has_many relation with the products. However, the url conveys different story...
You have to change the routes like:
resources :products do
resources :orders, only: [:new, :create, :destroy] do
collection do
get :success
get :fail
get :details
end
end
Then you will get the route
https://example.com/product_name/orders/new?params...
In my routes.rb I have
namespace :admin do
resources :clients do, only: [:index] do
resources :products, only: [:index, :new, :create]
end
resources :products, only: [:index]
end
Notice that I have two lines for resources :products. One is nested within resources :clients and the other as a top-level resource on :admin; each of these two has a different purpose in the application.
rake routes gives me:
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/products#index
POST /admin/clients/:client_id/products(.:format) admin/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/products#new
admin_products GET /admin/products(.:format) admin/products#index
I have a admin_client_products for the nested product#index resource. I also have admin_products for the top-level product#index resource. However, they point to the same controller action: admin/product#index.
Question: At this point, I need rails to deduce that these are two different actions. Using rails conventions, is there a way to tell rails that these two resources should have different controller actions i.e. one that should hit admin/products#index and the other should hit admin/clients/products#index?
The nested route should hit this:
class Admin::Clients::ProductsController < Admin::BaseController
def index; end
end
The top-level route should hit this:
class Admin::ProductsController < Admin::BaseController
def index; end
end
Definitely you can!
Here you need to customize your resourceful route by explicitly specifying a controller to use for the resource. The :controller option will let you do that.
So, in your case, specifying the controller as clients/products for the admin_clients_products resource would work in your desired way.
namespace :admin do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end # ------------------------------
resources :products, only: [:index]
end
rails routes will now give you what you want:
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/clients/products#index
POST /admin/clients/:client_id/products(.:format) admin/clients/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/clients/products#new
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_products GET /admin/products(.:format) admin/products#index
=========================
Extra bits:
If you want to omit the /admin portion from the url (I mean, if your application's routing design permits to), then you could use: scope module: 'admin' do...end like the following:
scope module: 'admin' do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end
resources :products, only: [:index]
end
and suddenly your routes will start looking awesome :)
client_products GET /clients/:client_id/products(.:format) admin/clients/products#index
POST /clients/:client_id/products(.:format) admin/clients/products#create
new_client_product GET /clients/:client_id/products/new(.:format) admin/clients/products#new
clients GET /clients(.:format) admin/clients#index
products GET /products(.:format) admin/products#index
I have to models User and Blog. Their respective urls are:
test.com/u/user_name # User
test.com/blogs # Blog
I'm trying to achieve that the blogs are nested to the user which created it. E.g. test.com/u/user_name/blogs and for an article test.com/u/user_name/blogs/article_name.
But the index for all blogs should respond to: test.com/blogs.
right now my routes are like this:
resources :users do
resources :blogs, except: [:index]
end
resources :blogs, only: [:index]
Which doesn't work.. What am i missing?
Routes
#config/routes.rb
resources :blogs, only: :index #-> domain.com/blogs -> blogs#index
resources :users, path: "u" do #-> domain.com/u/:id
resources :blogs #-> domain.com/u/:user_id/blogs/:id
end
You'll want to look up about the path argument for your routes
As a rule of thumb, you'll want to include any "non conventional" paths at the bottom of the routes file. As the routes are determined from the top to bottom, if you're capturing obscure paths near the top of the file, you'll end up causing conflict with your other routes
I'm trying to implement semi-static pages as per this railscast
At first I named my class 'About', but that threw the following error:
Invalid route name, already in use: 'page' (ArgumentError)
You may have defined two routes with the same name using the :as option, or you may be overriding a route already defined by a resource with the same naming.
After some googleing, it seemed like it was conflicted with active_admin for some reason, so I rename the table to 'Page' and I've carefully renamed all the appropriate files, classes and methods etc. from 'About' to 'Page'
This is my Page model:
class Page < ActiveRecord::Base
validates_uniqueness_of :url
def to_param
url
end
end
And these are my routes:
get 'signup', to: 'users#new', as: 'signup'
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
resources :users
resources :sessions
resources :password_resets
resources :posts do
resources :comments
resources :votes, only: [:new, :create]
resources :flags, only: [:new, :create]
end
resources :comments do
resources :comments
resources :votes, only: [:new, :create]
resources :flags, only: [:new, :create]
end
resources :newsletters
resources :pages, except: :show
resources :subscribers, only: [:index, :new, :create]
# resources :prelaunch
# get 'about', to: 'prelaunch#about'
root to: 'posts#index'
get ':id', to: 'pages#show', as: :page
I'm still getting the same error as described above.
The only way I can get it to half-work is by dropping the 'as: :page' bit, which stops the conflict, and hardcoding the url I want to point to into the code e.g.
<%= link_to page.name, "localhost:3000/#{page.url}" %>
which is far from ideal.
I can't find any help in Routing from the Outside In.
Could anyone help?
Here's the fix:
#config/routes.rb
resources :pages, except: :show
(remove get ':id', to: 'pages#show', as: :page)
This will create the standard RESTful routes, which will create a routing structure except the show action
Slugs
How to create app-wide slug routing for Rails app?
If you want to have /about etc, you'll have to generate them specifically:
#config/routes.rb
if Page.all.any?
Page.all.each do |page|
get page, to: "pages#show", id: page.id
end
end
This can also be handled with friendly_id
Have you considered using a gem that "does the work for you"? I've been using the https://github.com/thoughtbot/high_voltage gem to take care of static pages for me, without any hassle. It takes care of both routing and controller, only leaving the creation of the pages in a dedicated view/pages folder. Linking to a static page is as simple as creating a link to page_path(:name_of_the_page)
Ok, after a lot of hacking around and a helpful pointer from Rich Peck, I've got a working solution.
Routes:
resources :pages, except: :show
if Page.all.any?
Page.all.each do |page|
get "#{page.url}", to: "pages#show", as: "#{page.url}", id: page.id
end
end
Controller:
def show
#page = Page.find(params[:id])
end
Note, I've used the friendly_id gem as suggested.
To dynamically generate links:
Application controller:
def about_us
#pages = Page.all
end
helper_method :about_us
Pages helper:
def about_link(page)
link_to page.name, "/#{page.url}"
end
NB: - you need to include the / otherwise it will try to prepend the name of the controller for the page you're on (I'm not sure why).
My footer:
<% about_us.each do | page | %>
<%= about_link(page) %>
<% end %>
UPDATE:
I've had a lot of trouble deploying my app to Heroku, and I believe it's because of the pages routes.
I've now changed to a much simpler solution:
resources :pages, path: ""
and the problem's have gone away.