Problem with .save when model has two HABTM


#1

I have a problem with save on a model that contains two HABTM. When
create (save) only the categories are saved - the archives_medias table
stays untouched. On update both tables are updated.
This is how it looks now:

class Archive < ActiveRecord::Base
has_and_belongs_to_many :categories
has_and_belongs_to_many :medias

class ArchivesController < ApplicationController

def create
@archive = Archive.new(params[:archive])
if request.post? && @archive.save
  flash[:notice] = 'Archive was successfully created.'
  redirect_to :action => 'list'
else
  render :action => 'edit'
end

def update
@archive = Archive.find(params[:id])
if request.post? && @archive.update_attributes(params[:archive])
flash[:notice] = ‘Archive was successfully updated.’
redirect_to :action => ‘list’
else
render :action => ‘edit’
end

The values of categries and medias are passed through checkboxes useing
the categories_ids and media_ids arrays.

I can’t confirm it but I suspect this problem first accured after I
updated to 0.14.4. The model gets the data fine on both.

Strange?


#2

I have encountered the exact same problem and have yet to find a
solution. hopefully someone can figure this quirky behavior out.

-A


#3

Mattias B. wrote:

I have a problem with save on a model that contains two HABTM. When
create (save) only the categories are saved - the archives_medias table
stays untouched. On update both tables are updated.

Perhaps it’s related to the pluralization and singularization of
“media”.


We develop, watch us RoR, in numbers too big to ignore.


#4

Mark Reginald J. wrote:

Mattias B. wrote:

I have a problem with save on a model that contains two HABTM. When
create (save) only the categories are saved - the archives_medias table
stays untouched. On update both tables are updated.

Perhaps it’s related to the pluralization and singularization of
“media”.


We develop, watch us RoR, in numbers too big to ignore.

I have tried to specify both:

:class_name => ‘Media’,
:join_table => ‘archives_medias’,

No difference.


#5

pluralize(2,‘media’)

-> 2 medias


#6

Hi Everyone,

Oddly enough I was going to post something similar to this post. Here’s
my
situation and here’s what a friend and I have found:

There is a model with 4 HABTM relationships. When ‘create’ is executed
only
one HABTM is actually saved; the same one each time. The remaining
three
would only be saved in ‘update’.

Last night I found this in the ActiveRecord documents: Adding an object
to
a collection
(has_manyhttp://ar.rubyonrails.com/classes/ActiveRecord/Associations/ClassMethods.html#M000011or
has_and_belongs_to_manyhttp://ar.rubyonrails.com/classes/ActiveRecord/Associations/ClassMethods.html#M000014)
automatically saves that object, except if the parent object (the owner
of
the collection) is not yet stored in the database.

Not ideal, but like Mark Reginald J., we discovered saving the model
first then changing the associations and saving the model again produced
the
desired results.

So I went from ‘Why aren’t the other HABTMs saving?’ to ‘Why is that one
HABTM saving?’

Has anyone else experienced this situation?

Mel


#7

can someone post a code example of the best method to save the parent
then do the habtm relations?

thanks from this designer masquerading as a programmer,
-A


#8

Anthony Rudgick wrote:

I have encountered the exact same problem and have yet to find a
solution. hopefully someone can figure this quirky behavior out.

Interesting. I’ve also had problem with join table entries not
being added when the parent object is created. I fixed it by
adding the related objects after saving the parent object,
but I was unable to reproduce the problem in some experiments
with a simple pair of related models. So perhaps there is some
problem that only manifests in more complex code. I’m using only
one HABTM, not two however.


We develop, watch us RoR, in numbers too big to ignore.


#9

Just to clearify - this is also the behavior I see. Have also got it
working by saving the object twice. This just feels wrong when the first
HABTM is saved on the first save.


#10

Yep, just ran into this.

Its always the first HABTM relationship that saves. If I reorder them
the first one will save, but not the others. Really odd, but just
confirming this is the current behavior.

So I went from ‘Why aren’t the other HABTMs saving?’ to ‘Why is that one
HABTM saving?’

Has anyone else experienced this situation?

Mel


#11

Just to add to Mel’s comments (I’m working with him):

We could find no logical reason why one of our associations always
saved and the others never did on a creation of a new owner. For
example:

f = Foo.new
f.ass_1_ids = [1,2,3]
f.ass_2_ids = [1,2,3]
f.ass_3_ids = [1,2,3]

f.save

ass_1s always were persisted.
ass_2s and ass_3s never were.

I experimented with the issue in script/console for quite a while
last night, even switching dbs from Sqlite to MySql and even changing
our table column names to be totally rails conventional. Nothing
changed.

Interestingly, one of the associations that always failed on the web
app worked in the console.

Another datapoint is that the associations were fetched with now
problems when assigned to the owner (an excerpt from my console):

?> r = Recipe.new
=> #<Recipe:0x252c94c @attributes={“name”=>nil,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>nil, “workorder_id”=>nil}, @new_record=true>

r.name = ‘kinase recipe’
=> “kinase recipe”

r.description = ‘description’
=> “description”

assign the kinases:

r.kinase_ids=[1,2,3]
=> [1, 2, 3]

r
=> #<Recipe:0x252c94c @attributes={“name”=>“kinase recipe”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>“description”, “workorder_id”=>nil}, @new_record=true,
@kinases=[#<Kinase:0x251fe2c @attributes=
{“ref_inhib_compound_id”=>“10”, “name”=>“c-RAF(h)”,
“active_f”=>“17807”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“10”, “id”=>“1”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“45.00000000”,
“catalog_number”=>“c-RAF(h)”, “km_conc”=>“47.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x251fdf0 @attributes=
{“ref_inhib_compound_id”=>“1”, “name”=>“MEK1(h)”,
“active_f”=>“17809”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“1”, “id”=>“2”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“10.00000000”,
“catalog_number”=>“MEK1(h)”, “km_conc”=>“126.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x251fdb4 @attributes=
{“ref_inhib_compound_id”=>“30”, “name”=>“MAPK2(m)”,
“active_f”=>“17802”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“30”, “id”=>“3”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”,
“description”=>“155.00000000”, “catalog_number”=>“MAPK2(m)”,
“km_conc”=>“122.00000000”, “ref_inhib_known_value”=>“14”}>]>

the kinases were found!

As Mel said, our solution is to save the owner first, then add the
associations and save again.

For those who want more data, here’s a longer segment of my console
output, with one that works and one that doesn’t:

Create a new Recipe:

r = Recipe.new
=> #<Recipe:0x2586820 @attributes={“name”=>nil,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>nil, “workorder_id”=>nil}, @new_record=true>

r.name = ‘foo’
=> “foo”

r.description = ‘bar’
=> “bar”

Add some atp_concentrations (many to many)

r.atp_concentration_ids=[1,2]
=> [1, 2]

r
=> #<Recipe:0x2586820 @attributes={“name”=>“foo”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>“bar”, “workorder_id”=>nil}, @atp_concentrations=
[#<AtpConcentration:0x254c490 @attributes={“id”=>“1”,
“description”=>“KM”}>, #<AtpConcentration:0x254c454 @attributes=
{“id”=>“2”, “description”=>“10”}>], @new_record=true>

r.save
=> true

r.id
=> 15

r = Recipe.find 15
=> #<Recipe:0x25377c0 @attributes={“name”=>“foo”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil, “id”=>“15”,
“description”=>“bar”, “workorder_id”=>nil}>

r
=> #<Recipe:0x25377c0 @attributes={“name”=>“foo”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil, “id”=>“15”,
“description”=>“bar”, “workorder_id”=>nil}>

r.atp_concentrations
=> [#<AtpConcentration:0x252fd2c @attributes=
{“atp_concentration_id”=>“1”, “recipe_id”=>“15”, “id”=>“1”,
“description”=>“KM”}>, #<AtpConcentration:0x252fcf0 @attributes=
{“atp_concentration_id”=>“2”, “recipe_id”=>“15”, “id”=>“2”,
“description”=>“10”}>]

atp_concentrations worked fine on a Recipe.new… r.save

?> #### now for a kinase:
?> r = Recipe.new
=> #<Recipe:0x252c94c @attributes={“name”=>nil,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>nil, “workorder_id”=>nil}, @new_record=true>

r.name = ‘kinase recipe’
=> “kinase recipe”

r.description = ‘description’
=> “description”

r.kinase_ids=[1,2,3]
=> [1, 2, 3]

r
=> #<Recipe:0x252c94c @attributes={“name”=>“kinase recipe”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil,
“description”=>“description”, “workorder_id”=>nil}, @new_record=true,
@kinases=[#<Kinase:0x251fe2c @attributes=
{“ref_inhib_compound_id”=>“10”, “name”=>“c-RAF(h)”,
“active_f”=>“17807”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“10”, “id”=>“1”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“45.00000000”,
“catalog_number”=>“c-RAF(h)”, “km_conc”=>“47.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x251fdf0 @attributes=
{“ref_inhib_compound_id”=>“1”, “name”=>“MEK1(h)”,
“active_f”=>“17809”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“1”, “id”=>“2”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“10.00000000”,
“catalog_number”=>“MEK1(h)”, “km_conc”=>“126.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x251fdb4 @attributes=
{“ref_inhib_compound_id”=>“30”, “name”=>“MAPK2(m)”,
“active_f”=>“17802”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“30”, “id”=>“3”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”,
“description”=>“155.00000000”, “catalog_number”=>“MAPK2(m)”,
“km_conc”=>“122.00000000”, “ref_inhib_known_value”=>“14”}>]>

the kinases were found!

?> r.save!
=> true

now let’s refetch the data

?> r.id
=> 16

r = Recipe.find 16
=> #<Recipe:0x250d830 @attributes={“name”=>“kinase recipe”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil, “id”=>“16”,
“description”=>“description”, “workorder_id”=>nil}>

r.kinases
=> []

they were not saved

?> #### but if I add them to this object and do an update, they will
?> r.kinase_ids=[1,2,3]
=> [1, 2, 3]

r
=> #<Recipe:0x250d830 @attributes={“name”=>“kinase recipe”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil, “id”=>“16”,
“description”=>“description”, “workorder_id”=>nil}, @kinases=
[#<Kinase:0x2503010 @attributes={“ref_inhib_compound_id”=>“10”,
“name”=>“c-RAF(h)”, “active_f”=>“17807”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“10”, “id”=>“1”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“45.00000000”,
“catalog_number”=>“c-RAF(h)”, “km_conc”=>“47.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x2502fd4 @attributes=
{“ref_inhib_compound_id”=>“1”, “name”=>“MEK1(h)”,
“active_f”=>“17809”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“1”, “id”=>“2”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“10.00000000”,
“catalog_number”=>“MEK1(h)”, “km_conc”=>“126.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x2502f98 @attributes=
{“ref_inhib_compound_id”=>“30”, “name”=>“MAPK2(m)”,
“active_f”=>“17802”, “sort_order”=>“1”,
“ref_inhib_10atp_compound_conc”=>“30”, “id”=>“3”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”,
“description”=>“155.00000000”, “catalog_number”=>“MAPK2(m)”,
“km_conc”=>“122.00000000”, “ref_inhib_known_value”=>“14”}>]>

r.save
=> true

r.id
=> 16

r = Recipe.find 16
=> #<Recipe:0x24ef90c @attributes={“name”=>“kinase recipe”,
“experiment_type_id”=>nil, “replicate_type_id”=>nil, “id”=>“16”,
“description”=>“description”, “workorder_id”=>nil}>

r.kinases
=> [#<Kinase:0x24eb0dc @attributes={“ref_inhib_compound_id”=>“10”,
“name”=>“c-RAF(h)”, “active_f”=>“17807”, “sort_order”=>“1”,
“kinase_id”=>“1”, “recipe_id”=>“16”,
“ref_inhib_10atp_compound_conc”=>“10”, “id”=>“1”, “simple_name”=>nil,
“ref_inhib_100atp_compound_conc”=>“10”, “description”=>“45.00000000”,
“catalog_number”=>“c-RAF(h)”, “km_conc”=>“47.00000000”,
“ref_inhib_known_value”=>“14”}>, #<Kinase:0x24eb0a0 @attributes=
{“ref_inhib_compound_id”=>“1”, “name”=>“MEK1(h)”,
“active_f”=>“17809”, “sort_order”=>“1”, “kinase_id”=>“2”,
“recipe_id”=>“16”, “ref_inhib_10atp_compound_conc”=>“1”, “id”=>“2”,
“simple_name”=>nil, “ref_inhib_100atp_compound_conc”=>“10”,
“description”=>“10.00000000”, “catalog_number”=>“MEK1(h)”,
“km_conc”=>“126.00000000”, “ref_inhib_known_value”=>“14”}>, #<Kinase:
0x24eb064 @attributes={“ref_inhib_compound_id”=>“30”, “name”=>“MAPK2
(m)”, “active_f”=>“17802”, “sort_order”=>“1”, “kinase_id”=>“3”,
“recipe_id”=>“16”, “ref_inhib_10atp_compound_conc”=>“30”, “id”=>“3”,
“simple_name”=>nil, “ref_inhib_100atp_compound_conc”=>“10”,
“description”=>“155.00000000”, “catalog_number”=>“MAPK2(m)”,
“km_conc”=>“122.00000000”, “ref_inhib_known_value”=>“14”}>]


#12

Mel put this in our code base earlier. I’ve abbreviated the final save:

@recipe = Recipe.new(params[:recipe])
@recipe.save!

Ruby has a simple definition of truth. Any value that is not nil or

the constant false is true.

update any associations defined:

@recipe.kinase_ids = @params[:kinase_ids] == nil ? Array.new : @params
[:kinase_ids]
removed_email_address@domain.invalid
#go where you want
else
#go where you want
end