BACKTRACE

(condense)

Back to Posts List

GET

Rails Nested Resources Tutorial

Preface

The best way to get started with Rails 2.x nested routing and routing at all, is to read the official Rails Routing guide at the Rails Guides website.

Rails Nested Routes/Resources

The rails nested routes/resources were indroduced at Rails 1.2 as part of the RESTful approach that was adopted by the Rails core members.
Nested resources allowed urls like:
/tasks/?project_id=1

To have a bit more sexy/RESTful look:
/projects/1/tasks


Let�s use this example to make things a little more clear, we will use 2 models, Project and Task

  1. /app/models/project.rb
    class Project < ActiveRecord::Base
    has_many :tasks
    end
  1. /app/models/task.rb
    class Task < ActiveRecord::Base
    belongs_to :project
    end


Setting up routes.rb

Creating a Nested Resource

:has_many keyword

The easiest way to create a nested route, is to use the :has_many keyword like that:
# /config/routes.rb
map.resources :projects, :has_many =&gt; :tasks

# and the correspondent task resource
map.resources :tasks


Adding the second routes, that defines a RESTful route to :tasks, depends if you would like to allow an access to the Task resource, without the project context, this is not a must.

Block


You can also specify the sub-resources in a block

map.resources :projects do |project|
project.resources :tasks
end

Singular Resources

Just the same:

map.resources :projects do |project|
project.resource :design_document
end

Routes Helpers

Run:
$ rake routes

to see what kind of routes do you have in your application, you can pipe UNIX�s grep command (�| grep xxx�) to filter the results:
$ rake routes | grep project


A basic map.resources :projects will produce:
                 
events GET /projects
formatted_projects GET /projects.:format
POST /projects
POST /projects.:format
new_project GET /projects/new


but for a Nested route, like we defined before will produce:


project_tasks GET /projects/:project_id/tasks
new_project_task GET /projects/:project_id/tasks/new
edit_project_task GET /projects/:project_id/tasks/:id/edit
project_task GET /projects/:project_id/tasks/:id

sweet.

Singular Nested Route Helpers


(from the example above)

/projects – list all projects
/projects/1 – show a single project
/projects/1/design_document – a project�s design document


Using the routing helpers


Since we now have another resource in context when we want o use the new helpers, we need to include that resource instance as a paramter:

new_project_task(@project)

  1. or when both resources are required
    edit_project_task(project, @task) </pre> <br /> <h2>Forms</h2><br /> I'll assume you use form_for in your forms, it will make the usage of nested resources a lot easier than to work with plain HTML or form_tag.<br /> The regular form we know of form_for, receives one instance as the form object:<br /> <pre class="brush: rails"> &lt;% form_for(project) do |f| >

    <
    end >

But with nested resources, we’ll pimp it up a little bit:


< form_for([ @project, @task ]) do |f| >

<
end %>

Note the instances array, that specifies the objects we need in our form when we deal with nested resources

Controller


In the most basic way of nested resources, the nested resource is inaccessible without the context of the parent resource.
This example explains how to handle that kind of situation.
Well, we do know that we get at least 1 id when we deal with nested resources, the parent resource’s id, remember?

/projects/1/tasks/new

so we’ll add a before_filter to get the project instance:

	
  • on TasksControlller
    before_filter :get_project
  • private
    def get_project
    @project = Project.find(params[:project_id])
    end


    So we’ll have our parent resource whenever we do it. in our actions we can use the parent resource instance to create the nested resource:

    1. TasksController
      def create
      @project.tasks.build(params[:task])
      if @project.save
      respond_to …
      end
      else
      respond_to …
      end
      end
      end

    Conclusion and some Gotchas.


    Using nested resources and routes is the right thing, URLs are clear, and code is readable. but:

    POST

    blog comments powered by Disqus

    I Don't have cookies.

    ELAD ENV

    Variable Value
    LINKEDIN
    TWITTER
    FACEBOOK
    GITHUB
    WWR
    IRC
    { 'irc.freenode.net' => [ '#rubyonrails', '#railsbridge', '#ruby', '#mootools' ]}
    SKYPE
    eladmeidar

    You're seeing this error because I think it is funny.