This project is archived and is in readonly mode.
has_one :through not working?
Reported by jreiss | January 13th, 2009 @ 04:35 PM
Hi all! I'm having the strangest problem, here's the code, using Rails 2.2.2:
class User < ActiveRecord::Base
has_one :profile
has_one :contact, :through => :profile
end
class Contact < ActiveRecord::Base
has_one :profile
has_one :user, :through => :profile
end
class Profile < ActiveRecord::Base
belongs_to :contact
belongs_to :user
end
and the migrations:
create_table :users do |t|
t.string :username, :null => false
t.string :encrypted_password, :null => false
t.string :salt, :null => false
t.timestamps
end
create_table :contacts do |t|
t.string :first, :last, :null => false
t.string :address, :address2, :locality, :region, :country, :postal_code
t.string :website, :email, :company
t.timestamps
end
create_table :profiles do |t|
t.integer :user_id
t.integer :contact_id
t.timestamps
end
Now, if that's all correct (which i believe it is) then i dont understand why this happens:
u = User.new => #u.contact = Contact.new => #u.contact => nil u.profile = Profile.new => #u.profile.contact = Contact.new => #u.profile.contact => #u.contact => nil
BUT, if i do:
u = User.new => #u.save => true c = Contact.new => #c.save => true u.contact = c => #u.contact => #
Why do I need to save the records before assigning them? What's going on here?
Comments and changes to this ticket
-
Altair March 13th, 2009 @ 08:46 PM
I've been having the same problem with my models. Is this correct behavior?
It's quite annoying to have to set associations after the original model has been saved. I understand why this might be necessary, but I also believe that the :through association should be taking care of updating the record after the original model is saved.
Another time that this becomes a problem is in HABTM relationships that specify a :join_model.
-
Altair March 13th, 2009 @ 10:54 PM
Correction, HABTM still works in a test case I wrote up. It must be an application peculiarity of my own.
Still, has_one :through remains a problem.
-
Jacob Atzen March 15th, 2009 @ 01:40 PM
Basically this is just the way Rails works. What you want to do is probably something like:
u = User.new u.build_profile u.profile.build_contact u.save
-
Altair March 15th, 2009 @ 06:19 PM
I misread the original ticket, but there is, however, still a problem with has_one :through. Note that this issue is non-existent in a has_many :through.
Suppose that you have the same schema as outlined above. Executing the following results in incorrect behavior:
>> Profile.all => [] >> u = User.create => #<User id: 1, created_at: "2009-03-15 18:08:52", updated_at: "2009-03-15 18:08:52"> >> c = Contact.new => #<Contact id: nil, created_at: nil, updated_at: nil> >> c.user = u => #<User id: 1, created_at: "2009-03-15 18:08:52", updated_at: "2009-03-15 18:08:52"> >> Profile.all => [#<Profile id: 1, user_id: 1, contact_id: nil, created_at: "2009-03-15 18:09:21", updated_at: "2009-03-15 18:09:21">] >> c.save => true >> c.user => nil >> Profile.all => [#<Profile id: 1, user_id: 1, contact_id: nil, created_at: "2009-03-15 18:09:21", updated_at: "2009-03-15 18:09:21">]
What's happening here is that when you assign a :user to a :contact that has not yet been saved, an entry in :profile is created before the :contact even has an id (hence the nil for contact_id). With a has_many :through association, this is not an issue; entries in the association we are traversing :through are created only on the save. Clearly, the association we are going :through should not be creating records before a save on the parent model.
-
visnu June 12th, 2009 @ 12:55 AM
I ran into this same problem. has_one :through isn't honoring the contract of a normal has_one association. from the current documentation:
Assigning an object to a has_one association automatically saves that object and the object being replaced (if there is one), in order to update their primary keys - except if the parent object is unsaved (new_record? == true).
I've attached a test and a working patch.
-
visnu June 12th, 2009 @ 01:16 AM
- no changes were found...
-
visnu June 12th, 2009 @ 01:16 AM
- Tag changed from :through, has_one to :through, has_one, patch
-
Altair June 23rd, 2009 @ 06:03 AM
I haven't had a chance to apply your patch; I've moved on to other things.
Unfortunately I don't really think you can do much to draw more attention to the issue. The thing is that there are a couple ways you can work around the problem. What I ended up doing just as a short-term solution was using a has_many, and just enforcing that the "many" was only one.
You could try directly emailing some of the core developers, but I imagine they get all sorts of email every day, and you'll probably get lost in the shuffle. My guess is that this might have been caught already anyway, and will probably be fixed at least by the time 3.0 is released.
That all said, I'll see if I can scrounge up some time to take a look at testing your patch. After all, you did put the effort in. Which is appreciated, by the way.
-
Eloy Duran June 23rd, 2009 @ 07:11 AM
- Assigned user set to Michael Koziarski
- Milestone changed from 2.x to 2.3.4
I think Michael is the person for this one.
And indeed, tickets with fixes like these will definitely be taken care of. It's just hard to put a date on it. Thanks.
-
Wolfram Arnold June 29th, 2009 @ 10:54 PM
Has anyone tries setting :autosave => true? Would this fix it? I haven't tried this but from reading the issue this is what I'd try.
-
visnu August 8th, 2009 @ 09:26 PM
- Tag changed from :through, has_one, patch to :through, bugmash, has_one, patch
sneaking this into the bugmash spotlight
-
Rizwan Reza August 8th, 2009 @ 09:45 PM
verified
+1 This patch works in 2-3-stable, all tests pass. Does not work in master.
-
Greg Sterndale August 8th, 2009 @ 11:16 PM
+1 verified
has_one_through_for_new_records.diff patch applied and tests pass for 2-3-stable
patch fails to apply to master
-
Elad Meidar August 10th, 2009 @ 12:21 AM
+1 verified, +1 patch fixes on 2-3-stable with tests passing
on master, same issue and patch fails.
Loading development environment (Rails 3.0.pre) >> Profile.all => [] >> u = User.create => #<User id: 1, created_at: "2009-08-09 23:08:47", updated_at: "2009-08-09 23:08:47"> >> c = Contact.new => #<Contact id: nil, created_at: nil, updated_at: nil> >> c.user = u => #<User id: 1, created_at: "2009-08-09 23:08:47", updated_at: "2009-08-09 23:08:47"> >> Profile.all => [#<Profile id: 1, user_id: 1, contact_id: nil, created_at: "2009-08-09 23:08:57", updated_at: "2009-08-09 23:08:57">] >> c.save => true >> c.user => nil >> Profile.all => [#<Profile id: 1, user_id: 1, contact_id: nil, created_at: "2009-08-09 23:08:57", updated_at: "2009-08-09 23:08:57">]
-
Tristan Dunn August 10th, 2009 @ 12:41 AM
I verified the failure on master and I've attached a patch for master.
-
Elad Meidar August 10th, 2009 @ 12:45 AM
+1 Verified patch on master, applies cleanly and tests run
Loading development environment (Rails 3.0.pre) >> Profile.all => [] >> u = User.create => #<User id: 1, created_at: "2009-08-09 23:41:33", updated_at: "2009-08-09 23:41:33"> >> c = Contact.new => #<Contact id: nil, created_at: nil, updated_at: nil> >> c.user = u => #<User id: 1, created_at: "2009-08-09 23:41:33", updated_at: "2009-08-09 23:41:33"> >> Profile.all => [] >> c.save => true >> c.user => #<User id: 1, created_at: "2009-08-09 23:41:33", updated_at: "2009-08-09 23:41:48"> >> Profile.all => [#<Profile id: 1, user_id: 1, contact_id: 1, created_at: "2009-08-09 23:41:48", updated_at: "2009-08-09 23:41:48">]
-
Repository August 10th, 2009 @ 12:53 AM
- State changed from new to resolved
(from [ecc9b705d7d12f853c794ca564b8e72995563eae]) Allow ho:through#build when the owner is a new record [#1749 state:resolved]
Signed-off-by: Pratik Naik pratiknaik@gmail.com
http://github.com/rails/rails/commit/ecc9b705d7d12f853c794ca564b8e7... -
Repository August 10th, 2009 @ 12:53 AM
(from [a0f69722be00cd546558b067054e9e7ae2564274]) Allow ho:through#build when the owner is a new record [#1749 state:resolved]
Signed-off-by: Pratik Naik pratiknaik@gmail.com
http://github.com/rails/rails/commit/a0f69722be00cd546558b067054e9e... -
CancelProfileIsBroken August 10th, 2009 @ 02:15 AM
- Assigned user cleared.
- Tag changed from :through, bugmash, has_one, patch to :through, has_one, patch
- Milestone cleared.
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
- 1749 has_one :through not working? (from [ecc9b705d7d12f853c794ca564b8e72995563eae]) Allow h...
- 1749 has_one :through not working? (from [a0f69722be00cd546558b067054e9e7ae2564274]) Allow h...
- 3043 has_one :through not working with new_record, breaks validation Assigning to Michael since he was the name that came up i...