Collection_select and has_many :through

I am having a bit of an issue with updating multiple models from one form - and in particular the collection_select related to a has_many through relationship. I have the following models: (Location is a pseudo-class which really acts as the join model for the others.)

class Location < ActiveRecord::Base   belongs_to :address   belongs_to :organisation   belongs_to :address_type

Then the other models are as follows:

class Address < ActiveRecord::Base   has_many :locations   has_many :address_types, :through => :locations   has_many :organisations, :through => :locations

class Organisation < ActiveRecord::Base   has_many :locations   has_many :addresses, :through => :locations, :include=>:address_types

class AddressType < ActiveRecord::Base   has_many :locations   has_many :addresses, :through => :location

An organisation can have a number of different addresses (main, dispatch, invoice etc). And one address can belong to more than one organisation (to allow for different companies operating out of the same address), and an address can be more than one type of address (ie the invoice address could be the same as the dispatch address, but different to the main office address).

I have a form that allows a user to add a new address (uses Ryan Bates' "Handle Multiple Models in One Form" technique from the Advanced Rails Recipes book and it has a pulldown to allow the user to say what this address is.

Here is the relevant part of the Organisations model:   after_update :save_addresses

  def new_address_attributes=(address_attributes)     #handles the address edit form in the edit view     address_attributes.each do |attributes|         addresses.build(attributes)     end   end

  def existing_address_attributes=(address_attributes)     addresses.reject(&:new_record?).each do |address|       attributes = address_attributes[address.id.to_s]       if attributes         address.attributes = attributes       else         addresses.delete(address)       end     end   end

  def save_addresses     addresses.each do |address|       address.save(false)     end   end

And here is the addresses controller class AddressesController < ApplicationController   def new     @address = Address.new     @organisation = Organisation.find(params[:organisation_id])     @locations = Location.find_all_by_organisation_id(params [:organisation_id])     @address.locations.build     respond_to do |format|       format.html # new.html.erb       format.js {render :partial => 'form', :locals => { :address => @address, :organisation => @organisation, :location=>@locations }}

The problem I am having is that the save doesn't update the address_type_id in the locations table. Everything else is updated correctly, just not that one field. Being an old-fashioned PHP programmer, I am slightly struggling to find out how to make it update that field as well. It seems the problem lies in the collection_select in the form. And I am not sure how to construct this collection_select to get round this. The docs say: collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

which is not exactly clear ... however I have, through trial-and-error found that "method" appears to relate to the field or column of the table from which the data is drawn. So if I want the address_type_id to be passed, this means that the object must surely be built using the location object - except that doesn't work. I constantly get "NoMethodError" for anything in that field except ":id" - which is not what I want included in the parameters hash at all - I want the address_type_id in there. (I have tried debug location - and it is the correct object)

This is in the address/_form.erb.html which gets called by the AJAX call:     <% fields_for "organisation[new_address_attributes]", location do |location_form| %>       Address type <%= location_form.collection_select (:address_type_id, get_address_types(organisation.id), :id, :name, {}) %>

(the get_address_types thing is a function that only allows the values that haven't already been used - can't have an organisation with two invoice addresses for example).

I love it when Rails works - but it is an absolute bugger and a half when it doesn't as you can't get at anything. You can't see what is going on - so you can't see where to jump in to insert the data in the appropriate place - or how to pass it correctly in the first place. Days like this make me wish I had stuck with PHP ...

PS this is a re-post of an earlier post when I had thought the problem related to HABTM - but it doesn't appear to be.

Arrrrggghhh.... Why is it - that 20 seconds after posting a problem - you find at least a partial solution? My problem was passing the wrong information in the locations local variable -     @locations = Location.find_all_by_organisation_id(params [:organisation_id]) Since I was creating a new entry I needed a new one to work on in memory I think ... think it should be @locations = @address.locations.build

That seems to be passing the correct information over - it is just turning up in the wrong place now - it is coming into the new_address_attributes, but of course that is not an attribute of an address - it is an attribute of a location - so I think I need a function to handle that in the same way. This sort of thing makes PHP feel like a pair of old slippers - not very trendy, but secretly you love them

Arrrrggghhh.... Why is it - that 20 seconds after posting a problem - you find at least a partial solution? My problem was passing the wrong information in the locations local variable -     @locations = Location.find_all_by_organisation_id(params [:organisation_id]) Since I was creating a new entry I needed a new one to work on in memory I think ... think it should be @locations = @address.locations.build

That seems to be passing the correct information over - it is just turning up in the wrong place now - it is coming into the new_address_attributes, but of course that is not an attribute of an address - it is an attribute of a location - so I think I need a function to handle that in the same way. This sort of thing makes PHP feel like a pair of old slippers - not very trendy, but secretly you love them