Problem passing id to another model

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:


2:

House id is <%= @house.id %>


3:
4:

Your rooms:


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:inadd_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 \"#[email protected]}\" 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

House id is <%= @house.id %>

Your rooms:

<% if @house.rooms %>
    <% for room in @house.rooms %>
  • <%= 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.
  • <% end %>
<% else %>

You have not added any rooms yet

<% end %>

<%= render :partial => ‘rooms/room_form’ %>


<%= 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 %>

<%= form.label :name %>
<%= form.text_field :name %>

Lights

<% form.object.lights.build if form.object.lights.empty? %> <% form.fields_for :lights do |light_form| %> <%= render :partial => "light", :locals => { :form => light_form } %> <% end %>

<%= add_child_link "[+] Add new light", form, :lights %>

Small Appliances

<% 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 %>

<%= add_child_link "[+] Add new small appliance", form, :small_appliances %>

<%= form.submit "Submit" %>

<% 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 [email protected] 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

House id is <%= @house.id %>

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
[email protected] or

blog.railsjaipur.in

I’ll tell you a second solution.

Thanks pankaj88, that worked nicely.