Forum: Ruby on Rails Going round in circles trying to save a HABTM

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
esloane (Guest)
on 2005-12-08 02:39
(Received via mailing list)
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:in
`send'
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:in
`each'
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:in
`call'
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:in
`each'
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:in
`valid_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:in
`save_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:in
`transaction'
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:in
`transaction'
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:in `create'

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.
rails (Guest)
on 2005-12-08 02:51
(Received via mailing list)
Try something like this....

@job = Job.new(params[:job])
@job.addresses << Address.new(params[:address])
if @job.save
...
peter.j.donald (Guest)
on 2005-12-08 03:40
(Received via mailing list)
Hi,

On 12/8/05, Eric S. <removed_email_address@domain.invalid> 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 ;)

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
This topic is locked and can not be replied to.