Going round in circles trying to save a HABTM

G’Day Peter,
Thanks again for your offer of help and once again my apologies for the
prickly response(a result of hair-tearing frustration only).

My problem, really, is grasping the concept of how it all hangs
together, rather than a specific technical issue. So I’m just coming at
it one step at a time. My background is a long association with database
and client server going way back to the functional programming days - so
I can be a bit brick-headed with this stuff.

OK, here goes;
Ruby version: 1.8.2 Rails 0.14.3

I’m concentrating on 3 tables here but the same kind of relationships
occur throughout the database (currently 26 tables).
CREATE TABLE addresses (
id int(10) unsigned NOT NULL auto_increment,
address_1 varchar(45) collate latin1_general_ci default NULL,
address_2 varchar(45) collate latin1_general_ci default NULL,
suburb varchar(45) collate latin1_general_ci default NULL,
state varchar(5) collate latin1_general_ci default NULL,
country varchar(45) collate latin1_general_ci default NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
AUTO_INCREMENT=21 ;

CREATE TABLE jobs (
id int(10) unsigned NOT NULL auto_increment,
job_types_fkid int(10) unsigned NOT NULL default ‘0’,
job_number varchar(10) collate latin1_general_ci default NULL,
job_type_id int(10) unsigned default NULL,
address_id int(10) unsigned default NULL,
job_phase_id int(10) unsigned default NULL,
current_action_id int(11) NOT NULL default ‘0’,
closed tinyint(1) default NULL,
PRIMARY KEY (id,job_types_fkid),
KEY Jobs_FKIndex1 (job_types_fkid)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
AUTO_INCREMENT=3 ;

CREATE TABLE jobs_addresses (
job_id int(10) unsigned NOT NULL default ‘0’,
address_id int(10) unsigned NOT NULL default ‘0’,
PRIMARY KEY (job_id,address_id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

Models:
job.rb
class Job < ActiveRecord::Base
has_and_belongs_to_many :people
has_many :milestones
has_many :job_phases ##Note: tables named singular_plural are
NOT link tables but lookups i.e contain a description of a code
has_many :people
has_and_belongs_to_many :addresses, :join_table => “jobs_addresses”
has_many :reminders
has_many :discussions
has_many :actions
has_one :jobtype
has_many :documents
has_and_belongs_to_many :organisations
validates_associated :address

end

address.rb
class Address < ActiveRecord::Base
has_one :address_type
has_and_belongs_to_many :jobs, :join_table => “jobs_addresses”
has_one :organisation
has_many :people
end

I do actually have a job_address model
class JobAddress < ActiveRecord::Base
belongs_to :jobs
belongs_to :addresses
end
Though I’m not at all sure of the impact of that.

In the job_controller.rb I have
def new
@job = Job.new
@address = Address.new
end
And
def create
@job = Job.new(params[:job])
@address = Address.new(params[:address])
if @job.save & @address.save <= Line 54
flash[:notice] = ‘Job was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
(This version of create is just one of many that I’ve tried - but I’ll
stick with it to avoid confusion)

Running this I get;

NoMethodError in Job#create

undefined method `address’ for #Job:0x39d1ed8

RAILS_ROOT: ./script/…/config/…
Application Trace | Framework Trace | Full Trace

d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/base.rb:1497:in
method_missing' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:292:insend’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:292:in
validates_each' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:291:ineach’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:291:in
validates_each' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:288:incall’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:758:in
run_validations' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:752:ineach’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:752:in
run_validations' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:716:invalid_without_callbacks’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/callbacks.rb:306:in
valid?' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/validations.rb:686:insave_without_transactions’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/transactions.rb:126:in
save' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/transactions.rb:126:intransaction’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/transactions.rb:91:in
transaction' d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/transactions.rb:118:intransaction’
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.13.0/lib/active_record/transactions.rb:126:in
save' #{RAILS_ROOT}/app/controllers/job_controller.rb:54:increate’

Line 54 is “if @job.save & @address.save” and it is definitely @job.save
that throws the exception. Interestingly, in other iterations
@address.save works - @job.save still complains

Request

Parameters: {“commit”=>“Save Job Data”, “job”=>{“job_number”=>“G23”,
“closed”=>“false”, “job_phase_id”=>“1”, “job_type_id”=>“1”},
“address”=>{“country”=>“Orstraya”, “suburb”=>“Emerald”,
“address_1”=>“Level 42”, “address_2”=>“77, Sunset Strip”,
“state”=>“Vic”}}

So what is driving me nuts is how to code this relationship to update
the two main tables and the jobs_addresses link table.

On another topic I’m also confused about push_with_attributes and why it
does not appear to be a valid method within @job

@job.methods.sort
=> ["==", “===”, “=~”, “[]”, “[]=”, “id”, “send”, “`”,
“action_ids=”, “actions”, “actions=”, “actions_count”, “add_actions”,
“add_addresses”, “add_discussions”, “add_documents”, “add_job_phases”,
“add_milestones”, “add_organisations”, “add_people”, “add_reminders”,
“address_ids=”, “addresses”, “addresses=”, “addresses_count”,
“after_create”, “after_destroy”, “after_save”, “after_update”, "
after_validation",“after_validation_on_create”,“after_validation_on_update”,
“allow_concurrency”, “allow_concurrency=”, “attribute_names”,
“attribute_present?”, “attributes”, “attributes=”,
“attributes_before_type_cast”, “b64encode”, “before_create”,
“before_destroy”, “before_save”, “before_update”, “before_validation”,
“before_validation_on_create”, “before_validation_on_update”, “blank?”,
“build_jobtype”, “build_to_actions”, “build_to_discussions”,
“build_to_documents”, “build_to_job_phases”, “build_to_milestones”,
“build_to_people”, “build_to_reminders”, “call_allow_concurrency”,
“call_colorize_logging”, “call_configurations”,“call_default_timezone”,
“call_generate_read_methods”, “call_lock_optimistically”, “call_logger”,
“call_pluralize_table_names”, “call_primary_key_prefix_type”,
“call_record_timestamps”, “call_schema_format”,“call_table_name_prefix”,
“call_table_name_suffix”, “class”, “clear_association_cache”, “clone”,
“colorize_logging”, “colorize_logging=”, “column_for_attribute”,
“configurations”, “configurations=”, “connection”, “create”,
“create_in_actions”, “create_in_discussions”, “create_in_documents”,
“create_in_job_phases”, “create_in_milestones”, “create_in_people”,
“create_in_reminders”, “create_jobtype”, “create_or_update”, “create_or_
update_with_callbacks”,“create_with_callbacks”,“create_with_timestamps”,
“create_without_timestamps”, “decode64”, “decode_b”, “decrement”,
“decrement!”, “default_timezone”, “default_timezone=”, “destroy”,
“destroy_with_callbacks”, “destroy_with_transactions”,
“destroy_without_callbacks”, "destroy_without_transactions
", “discussion_ids=”, “discussions”, “discussions=”,
“discussions_count”, “display”, “document_ids=”, “documents”,
“documents=”, “documents_count”, “dup”, “encode64”, “eql?”, “equal?”,
“errors”, “extend”, “find_all_in_actions”, “find_all_in_discussions”,
“find_all_in_documents”, “find_all_in_job_phases”,
“find_all_in_milestones”, “find_all_in_people”, “find_all_in_reminders”,
“find_in_actions”, “find_in_discussions”, “find_in_documents”,
“find_in_job_phases”, “find_in_milestones”, “find_in_people”,
“find_in_reminders”, “freeze”, “frozen?”, “generate_read_methods”,
“generate_read_methods=”, “has_actions?”, “has_addresses?”,
“has_attribute?”, “has_discussions?”, “has_documents?”,
“has_job_phases?”, “has_jobtype?”, “has_milestones?”,
“has_organisations?”, “has_people?”, “has_reminders?”, “hash”, “id”,
“id=”, “id_before_type_cast”, “increment”, “increment!”, “initialize”,
“initialize_with_callbacks”, “inspect”,
“instance_eval”,“instance_of?”, “instance_variable_get”,
“instance_variable_set”, “instance_variables”, “is_a?”,
“is_complex_yaml?”, “job_phase_ids=”, “job_phases”, “job_phases=”,
“job_phases_count”, “jobtype”, “jobtype=”, “jobtype?”, “kind_of?”,
“load”, “lock_optimistically”,“lock_optimistically=”,
“locking_enabled?”, “logger”, “logger=”, “method”, “methods”,
“milestone_ids=”, “milestones”, “milestones=”, “milestones_count”,
“new_record?”, “nil?”, “object_id”, “organisation_ids=”,
“organisations”, “organisations=”, “organisations_count”, “people”,
“people=”, “people_count”, “person_ids=”,“pluralize_table_names”,
“pluralize_table_names=”, “primary_key_prefix_type”,
“primary_key_prefix_type=”, “private_methods”, “protected_methods”,
“public_methods”, “quoted_id”, “readonly!”, “readonly?”,
“record_timestamps”, “record_timestamps=”, “reload”, “reminder_ids=”,
“reminders”, “reminders=”, “reminders_count”, “remove_actions”,
“remove_addresses”, “remove_discussions”, “remove_documents”,
“remove_job_phases”, “remove_milestones”, “remove_organisations”,
“remove_people”, “remove_reminders”, “remove_subclasses_of”, “require”,
“require_gem”, “require_gem_with_options”, “require_library_or_gem”,
“respond_to?”, “respond_to_without_attributes?”, “returning”, “save”,
“save!”, “save_with_transactions”, “save_with_validation”,
“save_without_transactions”, “save_without_validation”, “schema_format”,
“schema_format=”, “send”, “set_jobtype_target”, “silence_stderr”,
“silence_warnings”, “singleton_methods”, “subclasses_of”, “suppress”,
“table_name_prefix”, “table_name_prefix=”, “table_name_suffix”,
“table_name_suffix=”, “taint”, “tainted?”, “to_a”, “to_param”, “to_s”,
“to_yaml”, “to_yaml_properties”, “to_yaml_type”, “toggle”, “toggle!”,
“transaction”, “type”, “untaint”, “update”, “update_attribute”,
“update_attribute_with_validation_skipping”,
“update_attribute_without_validation_skipping”, “update_attributes”,
“update_with_callbacks”, “update_with_lock”, “update_with_timestamps”,
“update_without_callbacks”, “update_without_timestamps”, “valid?”,
“valid_with_callbacks”, “valid_without_callbacks”, “validate”,
“validate_associated_records_for_actions”,
“validate_associated_records_for_addresses”,
“validate_associated_records_for_discussions”,
“validate_associated_records_for_documents”,
“validate_associated_records_for_job_phases”,
“validate_associated_records_for_milestones”,
“validate_associated_records_for_organisations”,
“validate_associated_records_for_people”,
“validate_associated_records_for_reminders”, “validate_on_create”,
“validate_on_update”]

OK - thats a lotta info for a daft wee problem - sorry if I’m inundating
you;~)

Kind Regards,
Eric.

Try something like this…

@job = Job.new(params[:job])
@job.addresses << Address.new(params[:address])
if @job.save

Hi,

On 12/8/05, Eric S. [email protected] wrote:

Thanks again for your offer of help and once again my apologies for the
prickly response(a result of hair-tearing frustration only).

We have all been there! I think I may have a better idea of what may
cause the error now … or maybe not :wink:

Your jobs table has a address_id column but your model does not use the
“belongs_to :address” macro but it does declare a validation on this
field via
“validates_associated :address”. This validation will thus fail and
cause that
error.

See if removing “validates_associated :address” or adding “belongs_to
:address”
fixes the problem. If not then we will need to look closer.

BTW I would kill JobAddress - I am not sure if it hurts but you don’t
need it.

Hope that helps.


Cheers,

Peter D.

Blog: http://www.RealityForge.org