Rails3 activerecord update_attributes не может сохранить foreign_key - PullRequest
1 голос
/ 08 июля 2011

В rails 3.0.9 / ruby ​​1.9.2 я получаю неожиданное поведение при попытке обновить внешние ключи модели.

Я начинаю с новой установки

$ rails new mytest
$ rails g model User 
$ rails g model Ad user_id:integer
$ rake db_migrate

Добавитьассоциация в приложении / models / Ad.rb

Class User < ActiveRecord::Base
  belongs_to :user
end

Теперь самое интересное.Я хочу изменить внешний ключ на объекте напрямую.Я вхожу в консоль рельсов с рельсами c

$ u = User.create
$ a=Ad.create(:user=>u)
$ a.update_attributes(:user_id => 9999)
$ a.user_id
=> 4

Так что это не работает.Я пытаюсь установить объект напрямую, а не передавать его для создания:

$ u = User.create
$ a=Ad.create
$ a.user = u
$ a.save
$ a.update_attributes(:user_id => 9999)
$ a.user_id
=> 5

Не работает.

Единственное, что работает, это:

$ u = User.create
$ a=Ad.create
$ a.user_id = u.id
$ a.save
$ a.update_attributes(:user_id => 9999)
$ a.user_id
=> 9999

Может кто-нибудь объяснить, что происходит, и как я могу изменить внешние ключи моих объектов?Я предполагаю, что «блокирующее» поведение происходит, когда создаются ассоциации объектов и возникает конфликт, поэтому один из ответов заключается в следующем:

$ Ad.find( a.id ).update_attributes( :user_id => xxxx )

Это работает.Но кажется, что для изменения внешнего ключа объекта нужно пройти долгий путь.Это также требует дополнительного попадания в БД, и, наконец, это довольно грязный код, если объект внутренне нуждается в обновлении.Что делать?

Ответы [ 3 ]

0 голосов
/ 08 июля 2011

Похоже, проблема в том, что вы пытаетесь изменить атрибут user_id напрямую.Поскольку вы добавили belongs_to :user в модель объявления, в Rails есть ряд дополнительных методов, помогающих справиться с ассоциацией.Вот страница из направляющих рельсов по методам belongs_to: http://edgeguides.rubyonrails.org/association_basics.html#belongs_to-association-reference

В этом случае вы можете использовать что-то вроде этого:

u1 = User.create
u2 = User.create
a=Ad.create(:user=>u)

a.user_id будет равно u1.id

Чтобы обновить ассоциацию, вы можете сделать это:

a.user = u2
a.save!

и теперь a.user_id должно равняться u2.id

Rails делает это, потому что предполагает, чтоассоциация - это не просто внешний ключ, так как объект User важнее внешнего внешнего ключа.

0 голосов
/ 09 июля 2011

Просто выгрузите загруженный объект перед обновлением

a = Ad.create( :user=>User.create )
a.user.reset   # Unload association object, the foreign key is untouched
a.update_attributes( :user_id=>9999 )
a.user_id
  => 9999

Вы можете сделать так, чтобы Factory Girl создавала объекты с незагруженными ассоциациями, такими как

def Factory.lazy( model )
    obj = Factory( model )
    obj.attributes.each{ |k,v|
    if assoc = k[/(.*)_id$/,1]
        eager = obj.send( assoc )
        eager.send("reset") unless eager.nil?
    end
    obj
}
0 голосов
/ 08 июля 2011

Ну, я думаю, что правильное объяснение требует от меня проверки исходного кода Rails и проверки того, что происходит с update_attributes, когда вы назначаете объект отношениям. Я проверю это позже.

Но я думаю, что правильно сделать не только обновить атрибут, но и весь объект пользователя. Что-то вроде:

u = User.create; a = Ad.create; a.user = u; a.save; a.user = User.find(9999); a.save

Или просто манипулируйте идентификаторами вашего объекта, как вы заметили, что он работает:

u = User.create; a = Ad.create; a.user_id = u.id; a.save; a.user_id = 9999; a.save
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...