Rails 3: How to create a new nested resource?

Ruby on-RailsRuby on-Rails-3

Ruby on-Rails Problem Overview


The Getting Started Rails Guide kind of glosses over this part since it doesn't implement the "new" action of the Comments controller. In my application, I have a book model that has many chapters:

class Book < ActiveRecord::Base
  has_many :chapters
end

class Chapter < ActiveRecord::Base
  belongs_to :book
end

In my routes file:

resources :books do
  resources :chapters
end

Now I want to implement the "new" action of the Chapters controller:

class ChaptersController < ApplicationController
  respond_to :html, :xml, :json
  
  # /books/1/chapters/new
  def new
    @chapter = # this is where I'm stuck
    respond_with(@chapter)
  end

What is the right way to do this? Also, What should the view script (form) look like?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

First you have to find the respective book in your chapters controller to build a chapter for him. You can do your actions like this:

class ChaptersController < ApplicationController
  respond_to :html, :xml, :json

  # /books/1/chapters/new
  def new
    @book = Book.find(params[:book_id])
    @chapter = @book.chapters.build
    respond_with(@chapter)
  end

  def create
    @book = Book.find(params[:book_id])
    @chapter = @book.chapters.build(params[:chapter])
    if @chapter.save
    ...
    end
  end
end

In your form, new.html.erb

form_for(@chapter, :url=>book_chapters_path(@book)) do
   .....rest is the same...

or you can try a shorthand

form_for([@book,@chapter]) do
    ...same...     

Solution 2 - Ruby on-Rails

Try @chapter = @book.build_chapter. When you call @book.chapter, it's nil. You can't do nil.new.

EDIT: I just realized that book most likely has_many chapters... the above is for has_one. You should use @chapter = @book.chapters.build. The chapters "empty array" is actually a special object that responds to build for adding new associations.

Solution 3 - Ruby on-Rails

Perhaps unrelated, but from this question's title you might arrive here looking for how to do something slightly different.

Lets say you want to do Book.new(name: 'FooBar', author: 'SO') and you want to split some metadata into a separate model, called readable_config which is polymorphic and stores name and author for multiple models.

How do you accept Book.new(name: 'FooBar', author: 'SO') to build the Book model and also the readable_config model (which I would, perhaps mistakenly, call a 'nested resource')

This can be done as so:

class Book < ActiveRecord::Base
  has_one :readable_config, dependent: :destroy, autosave: true, validate: true
  delegate: :name, :name=, :author, :author=, :to => :readable_config
  
  def readable_config
    super ? super : build_readable_config
  end
end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrewView Question on Stackoverflow
Solution 1 - Ruby on-RailsdombeszView Answer on Stackoverflow
Solution 2 - Ruby on-RailsAndrew VitView Answer on Stackoverflow
Solution 3 - Ruby on-RailsxxjjnnView Answer on Stackoverflow