Вопрос

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