In the previous post, we learned how nested resources work. Consider the following route configuration:
# config/routes.rb
resources :courses do
resources :lessons
end
It generates the following routes:
$ bin/rails routes -g lesson
Prefix Verb URI Pattern Controller#Action
course_lessons GET /courses/:course_id/lessons(.:format) lessons#index
POST /courses/:course_id/lessons(.:format) lessons#create
new_course_lesson GET /courses/:course_id/lessons/new(.:format) lessons#new
edit_course_lesson GET /courses/:course_id/lessons/:id/edit(.:format) lessons#edit
course_lesson GET /courses/:course_id/lessons/:id(.:format) lessons#show
PATCH /courses/:course_id/lessons/:id(.:format) lessons#update
PUT /courses/:course_id/lessons/:id(.:format) lessons#update
DELETE /courses/:course_id/lessons/:id(.:format) lessons#destroy
You must have noticed that the show
, edit
, update
, and destroy
routes for a lesson don't really need the course. They can exist on their own.
show
: Given a lesson id, show this lessonedit
: Given a lesson id, show the form to edit this lessonupdate
: Given a lesson id, update this lessondestroy
: Given a lesson id, delete this lesson
In contrast, the index
, create
, and new
routes for a lesson will always need a course, otherwise Rails won't know which course we're talking about.
index
: For which course we should show all the lessons?create
: Which course we should add this lesson to?new
: For which course we should add new lesson?
To address this, we could define our nested routes like this:
resources :courses do
resources :lessons, only: [:index, :new, :create]
end
resources :lessons, only: [:show, :edit, :update, :destroy]
However, Rails provides the shallow
option as a shortcut.
resources :courses do
resources :lessons, shallow: true
end
Shallow nesting strikes a balance between descriptive routes and deep nesting.
Let's review the generated routes.
$ bin/rails routes -g lesson
Prefix Verb URI Pattern Controller#Action
## Need a Course
course_lessons GET /courses/:course_id/lessons(.:format) lessons#index
POST /courses/:course_id/lessons(.:format) lessons#create
new_course_lesson GET /courses/:course_id/lessons/new(.:format) lessons#new
## Doesn't Need a Course
edit_lesson GET /lessons/:id/edit(.:format) lessons#edit
lesson GET /lessons/:id(.:format) lessons#show
PATCH /lessons/:id(.:format) lessons#update
PUT /lessons/:id(.:format) lessons#update
DELETE /lessons/:id(.:format) lessons#destroy
If you have multiple resources nested under a single parent resource, you can pass the shallow option to the parent.
resources :courses, shallow: true do
resources :lessons
resources :students
end
Shallow nesting lets you avoid deep nesting by generating the collection actions scoped under the parent, so as to get a sense of the hierarchy, but by not nesting the member actions. In other words, it only builds routes with the minimal amount of information to uniquely identify the resource.
That's a wrap. I hope you found this article helpful and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.