2013-10-26 10 views
0

has_one методы ассоциации выполняются непоследовательно для меня, и я не знаю почему.Rails: почему методы has_one-ассоциации ведут себя непоследовательно?

Возьмите две простые модели, которые связаны друг с другом:

class Container < ActiveRecord::Base 
    has_one: :super 
end 

class Super < ActiveRecord::Base 
    belongs_to: :container 
end 

Следующий код работает как надо:

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> 

При вызове container.super в коде выше, он возвращает недавно созданный экземпляр класса Super.

Следующий код, однако, не работает:

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 возвращает ноль, потому что кажется, ищет экземпляр супер в БД. Тем не менее, экземпляр еще не зафиксирован.

Но почему контейнер.super и Container.first.super не дают тот же результат, когда контейнер == Container.first?

+0

Вы пробовали вызов 'save' на экземпляр, созданный 'build_super'? Вероятно, почему Container.first.super не найден. Кроме того, я знаю, что это _probably_ не проблема здесь, но супер является зарезервированным словом и может вызвать некоторое безумие. – struthersneil

+0

Что происходит, так это то, что 'container.super' имеет ссылку на объект, который вы только что создали в ассоциации, даже если он не сохранен. 'Container.first.super' должен фактически выполнить чтение. – struthersneil

ответ

2

Container.first.build_super извлекает копию записи контейнера, создает экземпляр ассоциации и кэширует этот экземпляр ассоциации в этой копии записи Контейнера.

Вызов Container.first.super после этого выбирает отдельную копию записи Контейнера и находит, что у нее ничего нет для :super.

По сути, вы делаете это:

a = Container.first # New instance 
a.build_super   # Assign to :super 

b = Container.first # Separate instance 
b.super    # Nothing in :super 

То, что вы, вероятно, хотите сделать, это назначить эту переменный вместо извлечения другой копии:

container = Container.first 
container.build_super 
container.super 
+0

Отличное объяснение! Не видел, что я создавал новую копию всякий раз, когда я вызывал Container.first, но это имеет смысл. – earksiinni

2

В первом примере супер объект построен в памяти и потерян при перезагрузке его из базы данных (как показывает ваш второй пример). Если вы должны были сделать container.reload.super, чтобы загрузить его из базы данных в первом примере, объект в памяти будет потерян, и он будет равен нулю.

Боковое примечание. Это действительно плохая идея назвать вашу ассоциацию «супер». Это зарезервированное ключевое слово в Ruby для вызова методов родительских классов, когда подкласс отменяет его.

+0

+1 о "супер". – kristinalim

+0

* smacks head * Да, супер был не совсем лучшим выбором ... – earksiinni

Смежные вопросы