Nesting a resource: putting a "belongs to" relationship into the URL

Free preview 3 min to read

Where the earlier routes course stopped

Rails routes: basics, earlier in this series, showed resources :courses, param: :slug, only: [ :show ], this app's routing for public course pages. Every resources call in that course is a single, standalone resource; none of its seven lessons puts one resources block inside another. That course covered the seven-routes-in-one-line convention in depth and never reached the case where one resource can't exist without another.

A Catalog::Lesson is that kind of resource. Every lesson belongs to one course, and this app's admin routing says so in the URL itself.

The route, from this app's config/routes.rb

namespace :admin do
  namespace :catalog do
    resources :courses do
      resources :lessons, shallow: true do
        # ...
      end
    end
  end
end

One resources :lessons block, written inside the do ... end of resources :courses. bin/rails routes shows what that nesting produces for a lesson's create action:

admin_catalog_course_lessons  POST  /admin/catalog/courses/:course_id/lessons(.:format)  admin/catalog/lessons#create

:course_id is a new path segment that a standalone resources :lessons would never generate. Nesting one resource inside another does more than organize the routing file: it adds the parent's id to the URL, and Rails hands that id to the controller as params[:course_id], which Admin::Catalog::LessonsController#set_course reads:

def set_course
  @course = Admin::Catalog::FindCourseQuery.call(id: params[:course_id]).course
end

Why the URL says it at all

A lesson's create action needs to know which course it's adding to before it can do anything. Two separate resources, resources :courses and resources :lessons declared side by side, could still work: the request could carry a course id some other way, in a hidden field or the session. Nesting puts that requirement into the URL's shape instead, where it's visible, bookmarkable and impossible to leave out: /admin/catalog/courses/:course_id/lessons, not /admin/catalog/lessons. The relationship between a lesson and its course, which the database records (every Catalog::Lesson has a course_id foreign key), gets a matching shape in the routes that manage it.

What this course covers

Seven lessons, each checked against this app's config/routes.rb, its controllers and Rails' installed source. Lesson 2 covers shallow: true, the option already on this app's resources :lessons line, and what it flattens and leaves nested. Lesson 3 covers the singular resource keyword, with no plural and no :id, used four times in this routing block. Lesson 4 covers member do ... end, which this app's routes use twice for a custom reorder action. Lesson 5 covers module: and scope module:, which route a URL to a controller in a subdirectory. Lesson 6 shows how shallow: true treats a plural resource nested further down differently from a singular one, both in this block. Lesson 7 traces one nested route from URL to controller, tying the lessons together.

Lesson 1 of 7 · free

Keep going with lesson 2

resources :lessons, shallow: true: Rails' reason for the option

Rails' docs explain shallow: true, which keeps index, new and create nested and flattens member actions to :id.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...