This project is archived and is in readonly mode.
Adding records to a has_many collection does not work on initialization
Reported by Andreas Neuhaus | April 16th, 2008 @ 05:54 PM
(from Trac http://dev.rubyonrails.org/ticke.... As far as I know, this is still an issue)
class User < AR:Base
has_many :email_addresses
def email= (address)
email_addresses.build(:address => address)
end
end
class EmailAddress < AR:Base
belongs_to :user
end
I use User#email= to create an initial email address record for each user created. It works fine, except if you access User#email_addresses on non-saved user records:
# Works fine (creates user record and associated email address record):
User.create!(:username=>'foo', :email=>'foo@bar.baz')
# Also works fine:
u = User.new(:username=>'foo', :email=>'foo@bar.baz')
u.save!
# Does NOT work (user record saved, email address record lost):
u = User.new(:username=>'foo', :email=>'foo@bar.baz')
u.email_addresses # => []
u.save!
Is this a bug or intended behaviour? It looks like the has_many collection cache does not recognize associated objects that are build during initialization.
minaguib suggested a temp workaround:
def email= (address)
email_addresses(true) unless email_addresses.loaded? # Temp fix for for rails bug #9577
email_addresses.build(:address => address)
end
Some more info in my blog
Comments and changes to this ticket
-
josh April 16th, 2008 @ 08:16 PM
- Assigned user set to josh
I wrote up some ActiveRecord tests for it, however they are passing for me. Did you try this on edge? Or are my test cases not covering the same thing you are referring too?
-
josh April 16th, 2008 @ 08:17 PM
- State changed from new to open
-
Simon Jefford April 16th, 2008 @ 10:43 PM
I've had a quick poke around with this and I can't seem to get a failing test either. However, I was able to replicate the problem in a Rails app using the exact models and code described by zargony.
-
Simon Jefford April 17th, 2008 @ 01:36 PM
I can only get this to happen inside script/console.
Again, using the models described by Andreas, I ran the following using script/runner:
u = User.new(:username => 'foo', :email => 'foo@bar.baz') u.email_addresses u.save! puts u.email_addresses.first.address
and got
-
Andreas Neuhaus April 17th, 2008 @ 02:37 PM
Interestingly, the test also passes for me. But the bug still occurs when using the console of a project where I disabled the described workaround.
Though I don't see why, there must be something wrong with the test. I'll do some more tests later today.
-
Andreas Neuhaus April 17th, 2008 @ 03:33 PM
Joshua: yes, I'm trying with rails edge pulled from git.
The problem occurs, if you create an object of a model which builds associated objects during initialization. If you access these associated objects before saving the object itself, all associated objects are lost.
I guess, that this problem might be related to the lazy-loading of an associated collection. If an object builds associated objects in its initializer, the association is still marked as loaded?==false; so on first access to the association proxy, it'll try to load the collection and wipes the ones built in memory.
For some reason, it looks like accessing email_addresses.first is ok, but accessing email_addresses or doing email_addresses.inspect results in an empty array:
>> u=User.new(:username=>'foo', :email=>'foo@bar.baz') => #<User id: nil, username: "foo", created_at: nil, updated_at: nil> >> u.email_addresses.loaded? => false >> u.email_addresses.first => #<EmailAddress id: nil, address: "foo@bar.baz", user_id: nil, created_at: nil, updated_at: nil> >> u.email_addresses.loaded? => true >> u.email_addresses => [#<EmailAddress id: nil, address: "foo@bar.baz", user_id: nil, created_at: nil, updated_at: nil>] >> ?> u=User.new(:username=>'foo', :email=>'foo@bar.baz') => #<User id: nil, username: "foo", created_at: nil, updated_at: nil> >> u.email_addresses.loaded? => false >> u.email_addresses => [] >> u.email_addresses.loaded? => true
I can reproduce this with script/console and with script/runner, but for some reason not with a test...
-
josh April 19th, 2008 @ 10:38 PM
- State changed from open to hold
Please reopen this ticket once you've got some tests.
-
Andreas Neuhaus April 21st, 2008 @ 01:54 PM
I think I finally tracked it down. It's the inspect method of AssociationProxy that does a reload if the target isn't loaded yet and therefore wipes the collection in this case.
The console automatically does #inspect to display an object. That's why it always happened in the console but not in tests.
I attached a patch that fixes this problem and adds a proper test.
Unfortunately, I cannot reopen this ticket myself.
-
Michael Koziarski May 2nd, 2008 @ 11:36 PM
- State changed from hold to open
-
Repository May 8th, 2008 @ 06:07 AM
- State changed from open to resolved
(from [bcb090c56b842a76397e0ea32f54c942fd11910e]) Calling ActiveRecord#inspect on an unloaded association won't wipe the collection [#9 state:resolved]
Signed-off-by: Joshua Peek
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 221 Routing error with ActionView::Helpers::ActiveRecordHelper#form Showing projects/show.html.builder where line #9 ...
- 221 Routing error with ActionView::Helpers::ActiveRecordHelper#form Extracted source (around line #9):
- 291 GemDependency test broken GemDependency tests were broken @ #9c4f00350a61987afad50...