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.