Has_one/belongs_to -- accessing the subordinate

With a has_one/belongs_to relationship, what’s the best way to guarantee
that the belongs_to object gets created and is accessible alongside the
has_one object? I think the after_create callback is a good choice,
but I discovered an oddity while trying it.

F’rinstance, if every horse has a carriage:

ActiveRecord::Schema.define do
create_table(:horses) {|t| t.timestamps }
create_table(:carriages) {|t| t.timestamps; t.integer :horse_id }
end
class Horse < ActiveRecord::Base
after_create :create_carriage
has_one :carriage, :dependent => :destroy
private
def create_carriage
Carriage.create(:horse => self)
end
end
class Carriage < ActiveRecord::Base
belongs_to :horse
end

This works fine using create:

horse1 = Horse.create()
=> #<Horse id: 5, created_at: “2010-05-11 16:17:06”, updated_at:
“2010-05-11 16:17:06”>

horse1.carriage
=> #<Carriage id: 5, created_at: “2010-05-11 16:17:06”, updated_at:
“2010-05-11 16:17:06”, horse_id: 5>
========
It also works if you call Horse.new(), horse.save!() and then
horse.carriage():
========

seabiscuit = Horse.new()
=> #<Horse id: nil, created_at: nil, updated_at: nil>

seabiscuit.save!
=> true

seabiscuit.carriage
=> #<Carriage id: 6, created_at: “2010-05-11 16:19:29”, updated_at:
“2010-05-11 16:19:29”, horse_id: 6>
========
If you call horse.carriage before horse.save!(), it shows up as nil –
that’s to be expected since there’s nothing in the db to associate with.
But if you THEN call horse.save! followed by horse.carriage, it STILL
reports nil:
========

nellie = Horse.new
=> #<Horse id: nil, created_at: nil, updated_at: nil>

nellie.carriage
=> nil # no carriage because we haven’t called save! yet

nellie.save!
=> true

nellie.carriage
=> nil # whoa, nellie! where’s your carriage???
========
… despite this, a carriage really was created for nellie:
========

nellie
=> #<Horse id: 9, created_at: “2010-05-11 16:27:11”, updated_at:
“2010-05-11 16:27:11”>

Carriage.find_by_horse_id(nellie.id)
=> #<Carriage id: 9, created_at: “2010-05-11 16:27:11”, updated_at:
“2010-05-11 16:27:11”, horse_id: 9>
========

I can imagine why this is happening: Calling horse.carriage caches a
value and doesn’t bother to re-check the database after the save!.
Since I’m sure it’s not a bug ;), I must be doing something improper.

How would a seasoned expert explain this? (And is it documented
somewhere?)

Thanks.

  • ff

ActiveRecord does query caching by default.
If you use build instead of new when creating the child object the
problem is solved, at least for has_many associations. I have no idea
if this works with has_one associations.

Example of what I mean:

@user = User.new(:name => ‘bob’)
=> #<User id: nil, name: “bob”, age: nil, created_at: nil, updated_at:
nil>

@user.comments
=> []

@user.comments.build(:body => ‘my comment’)
=> #<Comment id: nil, user_id: nil, body: “my comment”, created_at:
nil, updated_at: nil>

@user.save!
=> true

@user.comments
=> [#<Comment id: 2, user_id: 3, body: “my comment”, created_at:
“2010-05-12 08:57:21”, updated_at: “2010-05-12 08:57:21”>]

Replace build with new, and @user.comments will still return [] after
@user has been saved.