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.