I have a rails application that models a house. house contains rooms and rooms have nested attributes for light and small_appliance. I have a calculator controller, which is how end users will access the application. My problem is that I can''t get the partial for adding rooms to submit correctly from calculator. The initial page lets the user enter house information, which is saved using save_house when submit is clicked. This also redirects the user to the add_rooms page, where they can add rooms to the house. add_rooms displays correctly, but when I click submit, I get this error: ------------------------------- RuntimeError in Calculator#add_room Showing app/views/calculator/add_rooms.html.erb where line #2 raised: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id Extracted source (around line #2): 1: <div id="addRooms"> 2: <p>House id is <%= @house.id %></p> 3: 4: <h3>Your rooms:</h3> 5: <% if @house.rooms %> RAILS_ROOT: C:/Users/ryan/Downloads/react Application Trace | Framework Trace | Full Trace C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb: 2:in `_run_erb_app47views47calculator47add_rooms46html46erb'' C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: 36:in `add_room'' C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: 33:in `add_room'' ------------------------------- This is odd to me, because when add_rooms first renders, it shows the house_id. I don''t understand why it isn''t passed after the form is submitted. Here''s the code: ------------------------------- #app/models/room.rb class Room < ActiveRecord::Base # schema { name:string, house_id:integer } belongs_to :house has_many :lights, :dependent => :destroy has_many :small_appliances, :dependent => :destroy validates_presence_of :name accepts_nested_attributes_for :lights, :reject_if => lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => true accepts_nested_attributes_for :small_appliances, :reject_if => lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => true end ------------------------------- #app/models/house.rb class House < ActiveRecord::Base has_many :rooms # validation code not included def add_room(room) rooms << room end end ------------------------------- #app/controllers/calculator_controller.rb class CalculatorController < ApplicationController def index end def save_house @house = House.new(params[:house]) respond_to do |format| if @house.save format.html { render :action => ''add_rooms'', :id => @house } format.xml { render :xml => @house, :status => :created, :location => @house } else format.html { render :action => ''index'' } format.xml { render :xml => @house.errors, :status => :unprocessable_entity } end end end def add_rooms @house = House.find(params[:id]) @rooms = Room.find_by_house_id(@house.id) rescue ActiveRecord::RecordNotFound logger.error("Attempt to access invalid house #{params[:id]}") flash[:notice] = "You must create a house before adding rooms" redirect_to :action => ''index'' end def add_room @room = Room.new(params[:room]) @house = @room.house respond_to do |format| if @room.save flash[:notice] = "Room \"#...@room.name}\" was successfully added." format.html { render :action => ''add_rooms'' } format.xml { render :xml => @room, :status => :created, :location => @room } else format.html { render :action => ''add_rooms'' } format.xml { render :xml => @room.errors, :status => :unprocessable_entity } end end rescue ActiveRecord::RecordNotFound logger.error("Attempt to access invalid house #{params[:id]}") flash[:notice] = "You must create a house before adding a room" redirect_to :action => ''index'' end def report flash[:notice] = nil @house = House.find(params[:id]) @rooms = Room.find_by_house_id(@house.id) rescue ActiveRecord::RecordNotFound logger.error("Attempt to access invalid house #{params[:id]}") flash[:notice] = "You must create a house before generating a report" redirect_to :action => ''index'' end end ------------------------------- #app/views/calculator/add_rooms.html.erb <div id="addRooms"> <p>House id is <%= @house.id %></p> <h3>Your rooms:</h3> <% if @house.rooms %> <ul> <% for room in @house.rooms %> <li> <%= h room.name %> has <%= h room.number_of_bulbs %> <%= h room.wattage_of_bulbs %> watt bulbs, in use for <%= h room.usage_hours %> hours per day. </li> <% end %> </ul> <% else %> <p>You have not added any rooms yet</p> <% end %> <%= render :partial => ''rooms/room_form'' %> <br /> </div> <%= button_to "Continue to report", :action => "report", :id => @house %> ------------------------------- #app/views/rooms/_room_form.html.erb <% form_for :room, @house.rooms.build, :url => { :action => :add_room } do |form| %> <%= form.error_messages %> <p> <%= form.label :name %><br /> <%= form.text_field :name %> </p> <h3>Lights</h3> <% form.object.lights.build if form.object.lights.empty? %> <% form.fields_for :lights do |light_form| %> <%= render :partial => "light", :locals => { :form => light_form } %> <% end %> <p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p> <h3>Small Appliances</h3> <% form.object.small_appliances.build if form.object.small_appliances.empty? %> <% form.fields_for :small_appliances do |sm_appl_form| %> <%= render :partial => "small_appliance", :locals => { :form => sm_appl_form } %> <% end %> <p class="addLink"><%= add_child_link "[+] Add new small appliance", form, :small_appliances %></p> <p><%= form.submit "Submit" %></p> <% end %> ------------------------------- #application_helper.rb module ApplicationHelper def remove_child_link(name, form) form.hidden_field(:_delete) + link_to_function(name, "remove_fields (this)") end def add_child_link(name, form, method) fields = new_child_fields(form, method) link_to_function(name, h("insert_fields(this, \"#{method}\", \"# {escape_javascript(fields)}\")")) end def new_child_fields(form_builder, method, options = {}) options[:object] ||form_builder.object.class.reflect_on_association(method).klass.new options[:partial] ||= method.to_s.singularize options[:form_builder_local] ||= :form form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |form| render(:partial => options[:partial], :locals => { options [:form_builder_local] => form }) end end end ------------------------------- Thanks, Ryan
On Oct 18, 12:15 am, ryan8720 <ryan8...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > This is odd to me, because when add_rooms first renders, it shows the > house_id. I don''t understand why it isn''t passed after the form is > submitted.Well from a cursory glance of your form, @house is nil because nowhere do you pass an :id parameter, so you end up doing @house = House.find (nil) If I''m reading correctly the first time the form is rendered you''re in the add_room action so house ends up being derived from params[:room] [:house_id] but in add_rooms @house is retrieved differently. Fred
Hi Ryan, The problem is you have''t actually passed the house id to the room because <p>House id is <%= @house.id %></p> just displays the house id to that page. But it won''t be submitted with the form If I am getting right you have a relationship in house model like has_many :rooms ok Let me explain that to get the house id in controller the house.id must be submitted to the controller You can do this by two ways 1. Submitting house.id via hidden field use <%= hidden_field :house_id%> Note to get this running it must be set when building new room object build using @house.rooms.build() OR Room.new(:house_id => @house.id) If this one didn''t work contact me at bagwanpankaj-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org or blog.railsjaipur.in I''ll tell you a second solution. On Oct 18, 4:15 am, ryan8720 <ryan8...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> I have a rails application that models a house. house contains rooms > and rooms have nested attributes for light and small_appliance. I have > a calculator controller, which is how end users will access the > application. > > My problem is that I can''t get the partial for adding rooms to submit > correctly from calculator. The initial page lets the user enter house > information, which is saved using save_house when submit is clicked. > This also redirects the user to the add_rooms page, where they can add > rooms to the house. > > add_rooms displays correctly, but when I click submit, I get this > error: > > ------------------------------- > > RuntimeError in Calculator#add_room > > Showing app/views/calculator/add_rooms.html.erb where line #2 raised: > > Called id for nil, which would mistakenly be 4 -- if you really wanted > the id of nil, use object_id > > Extracted source (around line #2): > > 1: <div id="addRooms"> > 2: <p>House id is <%= @house.id %></p> > 3: > 4: <h3>Your rooms:</h3> > 5: <% if @house.rooms %> > > RAILS_ROOT: C:/Users/ryan/Downloads/react > Application Trace | Framework Trace | Full Trace > > C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb: > 2:in `_run_erb_app47views47calculator47add_rooms46html46erb'' > C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: > 36:in `add_room'' > C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: > 33:in `add_room'' > > ------------------------------- > > This is odd to me, because when add_rooms first renders, it shows the > house_id. I don''t understand why it isn''t passed after the form is > submitted. > > Here''s the code: > > ------------------------------- > > #app/models/room.rb > > class Room < ActiveRecord::Base > # schema { name:string, house_id:integer } > belongs_to :house > has_many :lights, :dependent => :destroy > has_many :small_appliances, :dependent => :destroy > validates_presence_of :name > accepts_nested_attributes_for :lights, :reject_if => lambda { |a| > a.values.all?(&:blank?) }, :allow_destroy => true > accepts_nested_attributes_for :small_appliances, :reject_if => > lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => > true > end > > ------------------------------- > > #app/models/house.rb > > class House < ActiveRecord::Base > has_many :rooms > > # validation code not included > > def add_room(room) > rooms << room > end > > end > > ------------------------------- > > #app/controllers/calculator_controller.rb > > class CalculatorController < ApplicationController > def index > end > > def save_house > @house = House.new(params[:house]) > respond_to do |format| > if @house.save > format.html { render :action => ''add_rooms'', :id => @house } > format.xml { render :xml => @house, :status > => :created, :location => @house } > else > format.html { render :action => ''index'' } > format.xml { render :xml => @house.errors, :status > => :unprocessable_entity } > end > end > end > > def add_rooms > @house = House.find(params[:id]) > @rooms = Room.find_by_house_id(@house.id) > > rescue ActiveRecord::RecordNotFound > logger.error("Attempt to access invalid house #{params[:id]}") > flash[:notice] = "You must create a house before adding rooms" > redirect_to :action => ''index'' > end > > def add_room > @room = Room.new(params[:room]) > @house = @room.house > > respond_to do |format| > if @room.save > flash[:notice] = "Room \"#...@room.name}\" was successfully > added." > format.html { render :action => ''add_rooms'' } > format.xml { render :xml => @room, :status > => :created, :location => @room } > else > format.html { render :action => ''add_rooms'' } > format.xml { render :xml => @room.errors, :status > => :unprocessable_entity } > end > end > rescue ActiveRecord::RecordNotFound > logger.error("Attempt to access invalid house #{params[:id]}") > flash[:notice] = "You must create a house before adding a room" > redirect_to :action => ''index'' > end > > def report > flash[:notice] = nil > @house = House.find(params[:id]) > @rooms = Room.find_by_house_id(@house.id) > rescue ActiveRecord::RecordNotFound > logger.error("Attempt to access invalid house #{params[:id]}") > flash[:notice] = "You must create a house before generating a > report" > redirect_to :action => ''index'' > end > > end > > ------------------------------- > > #app/views/calculator/add_rooms.html.erb > > <div id="addRooms"> > <p>House id is <%= @house.id %></p> > > <h3>Your rooms:</h3> > <% if @house.rooms %> > <ul> > <% for room in @house.rooms %> > <li> > <%= h room.name %> has <%= h room.number_of_bulbs %> > <%= h room.wattage_of_bulbs %> watt bulbs, in use for > <%= h room.usage_hours %> hours per day. > </li> > <% end %> > </ul> > <% else %> > <p>You have not added any rooms yet</p> > <% end %> > > <%= render :partial => ''rooms/room_form'' %> > > <br /> > </div> > > <%= button_to "Continue to report", :action => "report", :id => @house > %> > > ------------------------------- > > #app/views/rooms/_room_form.html.erb > > <% form_for :room, @house.rooms.build, :url => { :action > => :add_room } do |form| %> > <%= form.error_messages %> > <p> > <%= form.label :name %><br /> > <%= form.text_field :name %> > </p> > > <h3>Lights</h3> > <% form.object.lights.build if form.object.lights.empty? %> > <% form.fields_for :lights do |light_form| %> > <%= render :partial => "light", :locals => { :form => light_form } > %> > <% end %> > <p class="addLink"><%= add_child_link "[+] Add new light", > form, :lights %></p> > > <h3>Small Appliances</h3> > <% form.object.small_appliances.build if > form.object.small_appliances.empty? %> > <% form.fields_for :small_appliances do |sm_appl_form| %> > <%= render :partial => "small_appliance", :locals => { :form => > sm_appl_form } %> > <% end %> > <p class="addLink"><%= add_child_link "[+] Add new small appliance", > form, :small_appliances %></p> > > <p><%= form.submit "Submit" %></p> > <% end %> > > ------------------------------- > > #application_helper.rb > > module ApplicationHelper > def remove_child_link(name, form) > form.hidden_field(:_delete) + link_to_function(name, "remove_fields > (this)") > end > > def add_child_link(name, form, method) > fields = new_child_fields(form, method) > link_to_function(name, h("insert_fields(this, \"#{method}\", \"# > {escape_javascript(fields)}\")")) > end > > def new_child_fields(form_builder, method, options = {}) > options[:object] ||> form_builder.object.class.reflect_on_association(method).klass.new > options[:partial] ||= method.to_s.singularize > options[:form_builder_local] ||= :form > form_builder.fields_for(method, options[:object], :child_index => > "new_#{method}") do |form| > render(:partial => options[:partial], :locals => { options > [:form_builder_local] => form }) > end > end > end > > ------------------------------- > > Thanks, > Ryan
Thanks pankaj88, that worked nicely. On Oct 17, 11:46 pm, pankaj88 <bagwanpan...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Hi Ryan, > > The problem is you have''t actually passed the house id to the room > because > <p>House id is <%= @house.id %></p> > > just displays the house id to that page. But it won''t be submitted > with the form > If I am getting right you have a relationship in house model like > has_many :rooms > > ok > Let me explain that to get the house id in controller the house.id > must be submitted to the controller > You can do this by two ways > > 1. Submitting house.id via hidden field > use <%= hidden_field :house_id%> > > Note to get this running it must be set when building new room object > build using > @house.rooms.build() OR > Room.new(:house_id => @house.id) > > If this one didn''t work contact me at > bagwanpan...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org or > > blog.railsjaipur.in > > I''ll tell you a second solution. > > On Oct 18, 4:15 am, ryan8720 <ryan8...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > I have a rails application that models a house. house contains rooms > > and rooms have nested attributes for light and small_appliance. I have > > a calculator controller, which is how end users will access the > > application. > > > My problem is that I can''t get the partial for adding rooms to submit > > correctly from calculator. The initial page lets the user enter house > > information, which is saved using save_house when submit is clicked. > > This also redirects the user to the add_rooms page, where they can add > > rooms to the house. > > > add_rooms displays correctly, but when I click submit, I get this > > error: > > > ------------------------------- > > > RuntimeError in Calculator#add_room > > > Showing app/views/calculator/add_rooms.html.erb where line #2 raised: > > > Called id for nil, which would mistakenly be 4 -- if you really wanted > > the id of nil, use object_id > > > Extracted source (around line #2): > > > 1: <div id="addRooms"> > > 2: <p>House id is <%= @house.id %></p> > > 3: > > 4: <h3>Your rooms:</h3> > > 5: <% if @house.rooms %> > > > RAILS_ROOT: C:/Users/ryan/Downloads/react > > Application Trace | Framework Trace | Full Trace > > > C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb: > > 2:in `_run_erb_app47views47calculator47add_rooms46html46erb'' > > C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: > > 36:in `add_room'' > > C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb: > > 33:in `add_room'' > > > ------------------------------- > > > This is odd to me, because when add_rooms first renders, it shows the > > house_id. I don''t understand why it isn''t passed after the form is > > submitted. > > > Here''s the code: > > > ------------------------------- > > > #app/models/room.rb > > > class Room < ActiveRecord::Base > > # schema { name:string, house_id:integer } > > belongs_to :house > > has_many :lights, :dependent => :destroy > > has_many :small_appliances, :dependent => :destroy > > validates_presence_of :name > > accepts_nested_attributes_for :lights, :reject_if => lambda { |a| > > a.values.all?(&:blank?) }, :allow_destroy => true > > accepts_nested_attributes_for :small_appliances, :reject_if => > > lambda { |a| a.values.all?(&:blank?) }, :allow_destroy => > > true > > end > > > ------------------------------- > > > #app/models/house.rb > > > class House < ActiveRecord::Base > > has_many :rooms > > > # validation code not included > > > def add_room(room) > > rooms << room > > end > > > end > > > ------------------------------- > > > #app/controllers/calculator_controller.rb > > > class CalculatorController < ApplicationController > > def index > > end > > > def save_house > > @house = House.new(params[:house]) > > respond_to do |format| > > if @house.save > > format.html { render :action => ''add_rooms'', :id => @house } > > format.xml { render :xml => @house, :status > > => :created, :location => @house } > > else > > format.html { render :action => ''index'' } > > format.xml { render :xml => @house.errors, :status > > => :unprocessable_entity } > > end > > end > > end > > > def add_rooms > > @house = House.find(params[:id]) > > @rooms = Room.find_by_house_id(@house.id) > > > rescue ActiveRecord::RecordNotFound > > logger.error("Attempt to access invalid house #{params[:id]}") > > flash[:notice] = "You must create a house before adding rooms" > > redirect_to :action => ''index'' > > end > > > def add_room > > @room = Room.new(params[:room]) > > @house = @room.house > > > respond_to do |format| > > if @room.save > > flash[:notice] = "Room \"#...@room.name}\" was successfully > > added." > > format.html { render :action => ''add_rooms'' } > > format.xml { render :xml => @room, :status > > => :created, :location => @room } > > else > > format.html { render :action => ''add_rooms'' } > > format.xml { render :xml => @room.errors, :status > > => :unprocessable_entity } > > end > > end > > rescue ActiveRecord::RecordNotFound > > logger.error("Attempt to access invalid house #{params[:id]}") > > flash[:notice] = "You must create a house before adding a room" > > redirect_to :action => ''index'' > > end > > > def report > > flash[:notice] = nil > > @house = House.find(params[:id]) > > @rooms = Room.find_by_house_id(@house.id) > > rescue ActiveRecord::RecordNotFound > > logger.error("Attempt to access invalid house #{params[:id]}") > > flash[:notice] = "You must create a house before generating a > > report" > > redirect_to :action => ''index'' > > end > > > end > > > ------------------------------- > > > #app/views/calculator/add_rooms.html.erb > > > <div id="addRooms"> > > <p>House id is <%= @house.id %></p> > > > <h3>Your rooms:</h3> > > <% if @house.rooms %> > > <ul> > > <% for room in @house.rooms %> > > <li> > > <%= h room.name %> has <%= h room.number_of_bulbs %> > > <%= h room.wattage_of_bulbs %> watt bulbs, in use for > > <%= h room.usage_hours %> hours per day. > > </li> > > <% end %> > > </ul> > > <% else %> > > <p>You have not added any rooms yet</p> > > <% end %> > > > <%= render :partial => ''rooms/room_form'' %> > > > <br /> > > </div> > > > <%= button_to "Continue to report", :action => "report", :id => @house > > %> > > > ------------------------------- > > > #app/views/rooms/_room_form.html.erb > > > <% form_for :room, @house.rooms.build, :url => { :action > > => :add_room } do |form| %> > > <%= form.error_messages %> > > <p> > > <%= form.label :name %><br /> > > <%= form.text_field :name %> > > </p> > > > <h3>Lights</h3> > > <% form.object.lights.build if form.object.lights.empty? %> > > <% form.fields_for :lights do |light_form| %> > > <%= render :partial => "light", :locals => { :form => light_form } > > %> > > <% end %> > > <p class="addLink"><%= add_child_link "[+] Add new light", > > form, :lights %></p> > > > <h3>Small Appliances</h3> > > <% form.object.small_appliances.build if > > form.object.small_appliances.empty? %> > > <% form.fields_for :small_appliances do |sm_appl_form| %> > > <%= render :partial => "small_appliance", :locals => { :form => > > sm_appl_form } %> > > <% end %> > > <p class="addLink"><%= add_child_link "[+] Add new small appliance", > > form, :small_appliances %></p> > > > <p><%= form.submit "Submit" %></p> > > <% end %> > > > ------------------------------- > > > #application_helper.rb > > > module ApplicationHelper > > def remove_child_link(name, form) > > form.hidden_field(:_delete) + link_to_function(name, "remove_fields > > (this)") > > end > > > def add_child_link(name, form, method) > > fields = new_child_fields(form, method) > > link_to_function(name, h("insert_fields(this, \"#{method}\", \"# > > {escape_javascript(fields)}\")")) > > end > > > def new_child_fields(form_builder, method, options = {}) > > options[:object] ||> > form_builder.object.class.reflect_on_association(method).klass.new > > options[:partial] ||= method.to_s.singularize > > options[:form_builder_local] ||= :form > > form_builder.fields_for(method, options[:object], :child_index => > > "new_#{method}") do |form| > > render(:partial => options[:partial], :locals => { options > > [:form_builder_local] => form }) > > end > > end > > end > > > ------------------------------- > > > Thanks, > > Ryan