質問

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?

役に立ちましたか?

解決

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

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top