I've three models Project, Card and Task.
project has_many :cards
card has_many :tasks
I've defined the route for building cards like following:
resources :projects, except: [:new, :edit, :show] do
resources :cards do
resources :tasks
end
end
It'll create path for cards as: projects/:project_id/cards/
It'll create path for tasks as: projects/:project_id/cards/:card_id/tasks
What I need is:
Card routes should be nested to Project. (which I currently have) and Task routes should nested to only Card like /cards/:card_id/tasks (which I need).
How can I achieve that?
Thanks in advance!
resources :projects, except: [:new, :edit, :show] do
resources :cards
end
resources :cards do
resources :tasks
end
This is what you are looking for
You can just do,
resources :projects, except: [:new, :edit, :show] do
resources :cards
end
And further try with defining each route for task by,
get '/cards/:card_id/tasks', to: 'tasks#index'
I did not test but should work, Sad is you have to define it for each specific route.
Related
In my routes.rb file, I have the following resources
resources :classrooms, only: [:index, :new, :create] do
resources :students, only: [:index, :edit, :update], controller: 'students' do
get :honor
end
end
My honor route becomes /classrooms/:classroom_id/students/:students_id/honor(.:format)
but I want to replace student_id to id so that I can easily use it in the students controllers.
Ideal route is /classrooms/:classroom_id/students/:id/honor(.:format)
You can do that specifying it's a member route. this way
get :honor, on: :member
or
member do
get :honor
end
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
Im trying to create API endpoints from my Post model . It should be able to read, update, delete and create posts.
But a post needs to be assosiated to a Topic in order for it to be created so i nested it under topics. Here are my routes
namespace :api do
namespace :v1 do
resources :users, only: [:index, :show, :create, :update]
resources :posts, only: [:show, :update, :destroy]
resources :topics, except: [:edit, :new] do
resources :posts, only: [:create]
end
end
end
api_v1_post GET /api/v1/posts/:id(.:format) api/v1/posts#show
PATCH /api/v1/posts/:id(.:format) api/v1/posts#update
PUT /api/v1/posts/:id(.:format) api/v1/posts#update
DELETE /api/v1/posts/:id(.:format) api/v1/posts#destroy
api_v1_topic_posts POST /api/v1/topics/:topic_id/posts(.:format) api/v1/posts#create
api_v1_topics GET /api/v1/topics(.:format) api/v1/topics#index
POST /api/v1/topics(.:format) api/v1/topics#create
api_v1_topic GET /api/v1/topics/:id(.:format) api/v1/topics#show
PATCH /api/v1/topics/:id(.:format) api/v1/topics#update
PUT /api/v1/topics/:id(.:format) api/v1/topics#update
DELETE /api/v1/topics/:id(.:format) api/v1/topics#destroy
my issue is im getting a "no routes match" error when i run my Rspec
I think the issue is with the relationship.
Please add the relationship between Topic and Post and then try.
class Topic < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
end
If the route you are trying to create is POST api/v1/topics/:topic_id/create_post going to TopicsController than you would want the nesting to look like this instead:
resources :topics, except: [:edit, :new] do
post 'create_post' => 'topics#create_post'
end
Try this out for creating your routes if you haven't had success yet. It should provide the output that you want.
resources :topics, except: [:edit, :new] do
collection do
post 'create_post'
end
end
I have following relationship routes:
resources :courses, only: [:index, :show] do
resources :enrols, only: [:new, :create]
resources :lectures, only: [:show]
end
resources :code_casts, :path => 'casts', :as => 'casts', only: [:index, :show]
resources :blogs, :path => 'blog', :as => 'blog', only: [:index, :show] do
resources :blog_votes, only: [:create, :destroy]
end
I want polymorphic comments in courses, lectures, code_casts and blog.
Problem is lecture has a parent of course so route will be course/course_id/lecture/id and blog path will blog/id where comments will have different show pages.
If I understand the problem correctly, there is nothing special about deeply nested resources. So you might need something like this
# routes.rb
concern :commentable do
resources :comments
end
resources :courses, only: [:index, :show] do
resources :enrols, only: [:new, :create]
resources :lectures, only: [:show], concerns: [:commentable]
end
resources :code_casts, :path => 'casts', :as => 'casts', only: [:index, :show]
resources :blogs, :path => 'blog', :as => 'blog', only: [:index, :show], concerns: [:commentable] do
resources :blog_votes, only: [:create, :destroy]
end
This will create nested comments resources for lectures and blogs.
Than you need to differentiate the path in a comments controller
# comments_controller
def create
Comment.create(commentable: commentable, other_params...) # assuming you have `commentable` polymorphic belongs_to
end
# a little more ugly than Ryan suggests
def commentable
if request.path.include?('blog') # be careful. any path with 'blog' in it will match
Blog.find(params[:id])
elsif params[:course_id] && request.path.include?('lecture')
Course.find(params[:course_id).leactures.find(params[:id]) # assuming Course has_many lectures
else
fail 'unnable to determine commentable type'
end
end
All 'magic' is in commentable method, where youare checking the path and determine which commentable object to pick. I use similar approach, but this exact code is written by memory without testing. Hopefully you've got the idea.
Good day all,
Can someone kindly assist me with nested resources and its best practice.
I would like to restrict my :events route to only :show and :index, is this the correct way of doing it?
resources :events do
resources :registrations, :except => [:index]
end
resources :events, :only => [:index, :show]
Is this the best way or the way more Rubyist would handle such thing? I've added two lines of resources :events or is there a way to combine it all in 1 block?
Thanks in advance.
Yes. You can combine it in one block like:
resources :events, only: [:index, :show] do
resources :registrations, except: :index
end