Ryan Tinsley
2009-Oct-14 14:58 UTC
Render partial with nested attributes in different model
Hi everyone, My previous question was posted here<http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/dddaeb075789d4b5>. I''m starting a new topic as the problems have shifted. I have a rails application that models a house. There is a house model that has_many rooms. A room has a house_id and a name. I''ve also used the complex-form-examples to give room nested attributes of lights and small_appliances. complex-form-examples uses RJS and partials to accomplish this. There is a controller called calculator that is what users will use to access the application. When the submit button on calculator is pressed, it saves house information and redirects to an add_rooms page (located in app/views/calculator/add_rooms.html.erb) where the user can add rooms to the house. The add_rooms page uses a partial from app/views/rooms/_room_form.html.erb. I can get this page to display by removing the add_child_link links. If I don''t remove them, I get this error: Showing app/views/rooms/_room_form.html.erb where line #13 raised: undefined method `reflect_on_association'' for NilClass:Class With add_child_link gone the page renders. However, when I click submit I get a new error message: ActiveRecord::AssociationTypeMismatch in CalculatorController#add_room SmallAppliance(#49096610) expected, got Array(#1560620) RAILS_ROOT: C:/Users/ryan/Downloads/react Application Trace | Framework Trace | Full Trace C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_proxy.rb:263:in `raise_on_type_mismatch'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in `replace'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in `each'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in `replace'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:1322:in `small_appliances='' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2744:in `send'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2744:in `attributes='' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2740:in `each'' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2740:in `attributes='' C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2438:in `initialize'' C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:31:in `new'' C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:31:in `add_room'' If I remove the small_application part, the same thing happens for light. I think it has something to do with accepts_nested_attributes_for in the room model. I need to get this page working so that rooms can be added to a house without using the scaffold built pages. Users won''t have access to those. I also need the house id to be saved as house_id in the rooms table. Here is the code: *app/models/room.rb* class Room < ActiveRecord::Base 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 # save_house is called when submit is # pressed in app/views/calculator/index.html.erb 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 @house = House.find(params[:id]) @room = Room.new(params[:room]) respond_to do |format| if @room.save @house.add_room(@room) @house.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 /> <%= button_to "Continue to report", :action => "report", :id => @house %> </div> *app/views/rooms/_room_form.html.erb* <% form_for :room, :url => { :action => :add_room, :id => @house } do |form| %> <%= form.error_messages %> <p> <%= form.label :name %><br /> <%= form.text_field :name %> </p> <h3>Lights</h3> <% form.fields_for :lights do |light_form| %> <%= render :partial => ''rooms/light'', :locals => { :form => light_form } %> <% end %> <p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p> <h3>Small Appliances</h3> <% form.fields_for :small_appliances do |sm_appl_form| %> <%= render :partial => ''rooms/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 *public/javascripts/application.js* function insert_fields(link, method, content) { var new_id = new Date().getTime(); var regexp = new RegExp("new_" + method, "g") $(link).up().insert({ before: content.replace(regexp, new_id) }); } function remove_fields(link) { var hidden_field = $(link).previous("input[type=hidden]"); if (hidden_field) { hidden_field.value = ''1''; } $(link).up(".fields").hide(); } Thanks, Ryan --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---