Reputation: 407
has_one association methods are executing inconsistently for me and I don't know why.
Take two simple models that are associated with each other:
class Container < ActiveRecord::Base
has_one: :super
end
class Super < ActiveRecord::Base
belongs_to: :container
end
The following code works as it should:
container = Container.create
...
container.build_super
...
=> #<Super id: nil, container_id: 1, content: nil, created_at: nil, updated_at: nil>
container.super
=> #<Super id: nil, container_id: 1, content: nil, created_at: nil, updated_at: nil>
When you call container.super in the above code, it returns the newly built instance of class Super.
The following code, however, doesn't work:
Container.create
...
=> #<Container id: 1, created_at: "2013-10-26 20:31:26", updated_at: "2013-10-26 20:31:26">
Container.first.build_super
...
=> #<Super id: nil, container_id: 1, content: nil, created_at: nil, updated_at: nil>
Container.first.super
Container Load (0.2ms) SELECT "containers".* FROM "containers" ORDER BY "containers"."id" ASC LIMIT 1
Super Load (0.1ms) SELECT "supers".* FROM "supers" WHERE "supers"."container_id" = ? ORDER BY "supers"."id" ASC LIMIT 1 [["container_id", 1]]
=> nil
Container.first.super returns nil because it appears be looking for the instance of Super in the the db. However, the instance hasn't been committed yet.
But why do container.super and Container.first.super not yield the same result when container == Container.first?
Upvotes: 0
Views: 105
Reputation: 51697
In your first example, the super object is built in memory and lost when you reload it from the database (as your second example demonstrates). If you were to do container.reload.super
to load it from the database in the first example, the in-memory object would be lost and it be nil.
Side note: It's a really bad idea to name your association "super". This is a reserved keyword in Ruby to call methods on parent classes when a subclass overrides it.
Upvotes: 2
Reputation: 3459
Container.first.build_super
fetches a copy of the Container record, builds the association instance, and caches this association instance in that copy of the Container record.
Calling Container.first.super
after this fetches a separate copy of the Container record, and finds that it does not have anything for :super
.
Essentially, you are doing this:
a = Container.first # New instance
a.build_super # Assign to :super
b = Container.first # Separate instance
b.super # Nothing in :super
What you probably want to do is assign this to a variable instead of fetching another copy:
container = Container.first
container.build_super
container.super
Upvotes: 2