Francis's Octopress Blog

A blogging framework for hackers.

Creating Nested Resources in Ruby on Rails 3 and Updating Scaffolding Links and Redirection

Creating nested resources in ruby on rails 3 and updating scaffolding links and redirection

In this article, I’ll walk through a basic Rails (3.2.x) setup for creating a nested resource for two models. Nested resources work well when you want to build out URL structure between two related models, and still maintain a RESTful convention. This code assumes you are running RVM to manage Ruby/Gem versions, and Git for version control.

Creating a new Rails project $ mkdir family # create rvm gemset $ echo “rvm use —create ruby-1.9.2@family” > family/.rvmrc $ cd family # install rails $ gem install rails # create new rails project $ rails new . # version control $ git init $ git add . $ git commit -am “new rails project” Create two models (Parent & Child)

Parent model

$ rails generate scaffold Parent name:string $ git add . $ git commit -am “rails generate scaffold Parent name:string”

Child model

$ rails generate scaffold Child name:string parent_id:integer $ git add . $ git commit -am “rails generate scaffold Child name:string parent_id:integer”

Create db (defaults to SQLite3)

$ rake db:migrate

version control

$ git add db/schema.rb $ git commit db/schema.rb -m “created database schema” Review un-nested routes $ rake routes children GET /children(.:format) children#index

        POST   /children(.:format)          children#create

new_child GET /children/new(.:format) children#new edit_child GET /children/:id/edit(.:format) children#edit

  child GET    /children/:id(.:format)      children#show
        PUT    /children/:id(.:format)      children#update
        DELETE /children/:id(.:format)      children#destroy
parents GET    /parents(.:format)           parents#index
        POST   /parents(.:format)           parents#create

new_parent GET /parents/new(.:format) parents#new edit_parent GET /parents/:id/edit(.:format) parents#edit

 parent GET    /parents/:id(.:format)       parents#show
        PUT    /parents/:id(.:format)       parents#update
        DELETE /parents/:id(.:format)       parents#destroy

Adding model relationships

file: app/models/parent.rb

class Parent < ActiveRecord::Base attr_accessible :name has_many :children end

file: app/models/child.rb

class Child < ActiveRecord::Base attr_accessible :name, :parent_id belongs_to :parent end

version control

$ git commit app/models -m “added relationships to models” Nesting the routes $ rake routes parent_children GET /parents/:parent_id/children(.:format) children#index

              POST   /parents/:parent_id/children(.:format)          children#create

new_parent_child GET /parents/:parent_id/children/new(.:format) children#new edit_parent_child GET /parents/:parent_id/children/:id/edit(.:format) children#edit

 parent_child GET    /parents/:parent_id/children/:id(.:format)      children#show
              PUT    /parents/:parent_id/children/:id(.:format)      children#update
              DELETE /parents/:parent_id/children/:id(.:format)      children#destroy
      parents GET    /parents(.:format)                              parents#index
              POST   /parents(.:format)                              parents#create
   new_parent GET    /parents/new(.:format)                          parents#new
  edit_parent GET    /parents/:id/edit(.:format)                     parents#edit
       parent GET    /parents/:id(.:format)                          parents#show
              PUT    /parents/:id(.:format)                          parents#update
              DELETE /parents/:id(.:format)                          parents#destroy

Adding test data via Rails console $ rails c

dad = Parent.new(:name => ‘Paul’) => #

dad.save (0.1ms) begin transaction SQL (20.0ms) INSERT INTO “parents” (“created_at”, “name”, “updated_at”) VALUES (?, ?, ?) [[“created_at”, Fri, 06 Apr 2012 16:13:17 UTC +00:00], [“name”, “Paul”], [“updated_at”, Fri, 06 Apr 2012 16:13:17 UTC +00:00]] (2.4ms) commit transaction => true

son = dad.children.new(:name => ‘Eric’) => #

daughter = dad.children.new(:name => ‘Mara’) => #

exit Adding a private controller method to load the Parent object for each method

file: app/controllers/children_controller.rb

@@ -1,4 +1,7 @@ class ChildrenController < ApplicationController + + before_filter :load_parent + # GET /children # GET /children.json def index @@ -80,4 +83,11 @@ class ChildrenController < ApplicationController

   format.json { head :no_content }
 end

end + + private + + def load_parent + @parent = Parent.find(params[:parent_id]) + end + end At this point, each controller and view for the Child class model needs to be adjusted (links, redirection, form, etc)

Method: children#index

file: app/controllers/children_controller.rb

def index – @children = Child.all + @children = @parent.children.all

file: app/views/children/index.html.erb

  • <%= link_to ‘Show’, child %>
  • <%= link_to ‘Edit’, edit_child_path(child) %>
  • <%= link_to ‘Destroy’, child, confirm: ‘Are you sure?’, method: :delete %>
  • <%= link_to ‘Show’, parent_child_path(@parent, child) %>
  • <%= link_to ‘Edit’, edit_parent_child_path(@parent, child) %>
  • <%= link_to ‘Destroy’, [@parent, child], confirm: ‘Are you sure?’, method: :delete %>

–<%= link_to ‘New Child’, new_child_path %> +<%= link_to ‘New Child’, new_parent_child_path(@parent) %> Method: children#new

file: app/controllers/children_controller.rb

def new – @child = Child.new + @child = @parent.children.new

file: app/views/children/_form.html.erb

–<%= form_for(@child) do |f| %> +<%= form_for([@parent, @child]) do |f| %>

file: app/views/children/new.html.erb

–<%= link_to ‘Back’, children_path %> +<%= link_to ‘Back’, parent_children_path(@parent) %> Method: children#create

file: app/controllers/children_controller.rb

def create – @child = Child.new(params[:child]) + @child = @parent.children.new(params[:child])

 respond_to do |format|
   if @child.save
  •  format.html { redirect_to @child, notice: 'Child was successfully created.' }
    
  •  format.html { redirect_to [@parent, @child], notice: 'Child was successfully created.' }
    

    Method: children#show

file: app/controllers/children_controller.rb

def show – @child = Child.find(params[:id]) + @child = @parent.children.find(params[:id])

file: app/views/children/show.html.erb

–<%= link_to ‘Edit’, edit_child_path(@child) %> | –<%= link_to ‘Back’, children_path %> +<%= link_to ‘Edit’, edit_parent_child_path(@parent, @child) %> | +<%= link_to ‘Back’, parent_children_path(@parent) %> Method: children#edit

file: app/controllers/children_controller.rb

def edit – @child = Child.find(params[:id]) + @child = @parent.children.find(params[:id])

file: app/views/children/edit.html.erb

–<%= link_to ‘Show’, @child %> | –<%= link_to ‘Back’, children_path %> +<%= link_to ‘Show’, parent_child_path(@parent, @child) %> | +<%= link_to ‘Back’, parent_children_path(@parent) %> Method: children#update

file: app/controllers/children_controller.rb

def update – @child = Child.find(params[:id]) + @child = @parent.children.find(params[:id])

 respond_to do |format|
   if @child.update_attributes(params[:child])
  •  format.html { redirect_to @child, notice: 'Child was successfully updated.' }
    
  •  format.html { redirect_to [@parent, @child], notice: 'Child was successfully updated.' }
    

    Method: children#destroy

file: app/controllers/children_controller.rb

def destroy – @child = Child.find(params[:id]) + @child = @parent.children.find(params[:id])

 @child.destroy

 respond_to do |format|
  • format.html { redirect_to children_url }
  • format.html { redirect_to parent_children_path(@parent) } At this point, the default scaffolding’s links and redirection have been updated to work with the nested routes.