views:

495

answers:

2

Interesting segment of code that I can't get to work. I have the following models/relationships (unnecessary code excluded)

class Service < ActiveRecord::Base
  belongs_to :service_category, :foreign_key => "cats_uid_fk"
  belongs_to :service_type, :foreign_key => "types_uid_fk"
  has_and_belongs_to_many :service_subtypes, :join_table => "services_to_service_subs"
  belongs_to :service_request, :foreign_key => "audits_uid_fk"

  accepts_nested_attributes_for :service_subtypes
end

class ServiceSubtype < ActiveRecord::Base
  belongs_to :service_types, :foreign_key => "types_uid_fk"
  has_and_belongs_to_many :services, :join_table => "services_to_service_subs"
end

The form displaying all this info:

<% form_for(@request, :url => { :action => :create }) do |form| %>
 <table>   

...other data...

 <% form.fields_for :services do |fields| %>
  <%= fields.hidden_field :cats_uid_fk %>
  <%= fields.hidden_field :types_uid_fk %>
  <% fields.fields_for :service_subtypes do |subtype| %>
   <%= subtype.hidden_field :id %>
  <% end %> 
 <% end %>   

 <p>
   <%= form.submit "Create", :class=>"hargray" %>
 </p>         
<% end %>

And the controller to handle the submit:

def create
 logger.debug params[:service_request].inspect

 @request = ServiceRequest.new(params[:service_request])
 if session[:cus_id]
  @request.customer = Customer.find session[:cus_id]
 end

 begin      
  @request.save!
  flash[:notice] = "Information submitted successfully. You will be contacted by a customer service representative regarding the services you selected."
  redirect_to :controller => "customer", :action => "index"
 rescue Exception => exc
  flash[:notice] = "#{ format_validations(@request) } - #{exc.message}"
  render :action => "new"
 end

end

The html looks clean:

<input id="service_request_services_attributes_0_cats_uid_fk" name="service_request[services_attributes][0][cats_uid_fk]" type="hidden" value="1" />
  <input id="service_request_services_attributes_0_types_uid_fk" name="service_request[services_attributes][0][types_uid_fk]" type="hidden" value="1" />
  <input id="service_request_services_attributes_0_service_subtypes_attributes_0_id" name="service_request[services_attributes][0][service_subtypes_attributes][0][id]" type="hidden" value="2" />
   <input id="service_request_services_attributes_0_service_subtypes_attributes_0_id" name="service_request[services_attributes][0][service_subtypes_attributes][0][id]" type="hidden" value="2" />
  <input id="service_request_services_attributes_0_service_subtypes_attributes_1_id" name="service_request[services_attributes][0][service_subtypes_attributes][1][id]" type="hidden" value="4" />
   <input id="service_request_services_attributes_0_service_subtypes_attributes_1_id" name="service_request[services_attributes][0][service_subtypes_attributes][1][id]" type="hidden" value="4" />

The submitted parameters look like this:

{
...other data...
 "services_attributes"=> {
  "0"=> {
   "types_uid_fk"=>"1", 
   "service_subtypes_attributes"=> {
    "0"=>{"id"=>"1"}, 
    "1"=>{"id"=>"2"}, 
    "2"=>{"id"=>"3"}
   }, 
   "cats_uid_fk"=>"1"
  }
 }
}

I get back "undefined method 'service_subtype' for #" error and the only table not updated is the join table between the HABTM models. Any idea how to solve this or what is happening behind the scenes? I'm not sure I understand the "magic" happening behind this procedure to see it working. It seems like most say that HABTM doesnt work with nested attributes. Seems to be the case. Work arounds?

+1  A: 

Assuming this isn't a copy paste error in the Service model, it could be the source of your problem.

 accepts_nested_attributes_for :services_subtypes

Should be

 accepts_nested_attributes_for :service_subtypes

The first arguments to accepts_nested_attributes_for should be an association as defined by a has_many, has_and_belongs_to_many or belongs_to statement.

Your second minor problem about the double generating of the hidden field comes from your inserting it into the fields_for section. fields_for automatically includes a hidden field for the id. Making it safe to remove the hidden field line from the following block.

<% fields.fields_for :service_subtypes do |subtype| %>
  <%= subtype.hidden_field :id %>
<% end %>
EmFi
looks like the actual error is: Error inputting user: - undefined method `service_subtype' for #<Service:0xb1b6cba8>
Lukas
removed the typo. was a copy/paste error.
Lukas
The code you've posted shouldn't cause that error. Maybe there's a validation/callback in Service that calls service_subtype instead of service_subtypes?
EmFi
added a the post params for inspection
Lukas
Honestly, I can't diagnose any further without seeing the entire model or the stack trace. The first thing you should figure out when you get any error is "Which statement is causing causing it?"
EmFi
A: 

Found that error, was in my mailers. In any case, the fields_for :subtypes was still not generating the right parameters for the magic of nested attributes to pick up on what I was trying to do.

What I end up with is:

new.erb

<% form.fields_for :services do |fields| %>
 <%= fields.hidden_field :wsi_web_serv_cats_uid_fk %>
 <%= fields.hidden_field :wsi_web_serv_types_uid_fk %>
 <%= fields.hidden_field :service_subs_hash %>
<% end %>

service.rb

def service_subs_hash
 self.service_subtype_ids.join(", ")
end

def service_subs_hash=(ids)
 self.service_subtype_ids = ids.split(",")
end

This is kinda hackish I feel and I'm not sure I'm totally satisfied with it as the answer, but it puts a comma separated list in my hidden field that I can parse to service_subtype_ids again on submit.

If anyone knows how to do it without this extra virtual param, I'd love to know.

Thanks for the help.

Lukas