2011-07-08 40 views
1

在rails 3.0.9/ruby​​ 1.9.2当我尝试更新模型外键时,出现意外的行为。Rails3 activerecord update_attributes无法保存foreign_key

我开始一个新的安装在app /型号

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

添加关联/ Ad.rb

Class User < ActiveRecord::Base 
    belongs_to :user 
end 

现在到了有趣的部分。我想直接更改对象上的外键。我进入轨道控制台轨道ç

$ 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) 

这是有效的。但是,在一个对象上更改外键看起来还有很长的路要走。它还需要额外的数据库命中,最后如果对象内部需要自行更新,则代码非常混乱。该怎么办?

+0

顺便说一下之前就卸载加载的对象:我就遇到了这个问题的原因是,我从机械师切换到FactoryGirl。机械师不会在创建时加载关联的对象,但FactoryGirl会这样做。 – user681814

回答

0

做更新

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 

可以使工厂的女孩创建卸载关联对象这样

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

嗯,我认为正确的解释要求我检查Rails的源代码,并检查它在update_attributes中发生的情况。我会在稍后检查。

但我想正确的事情不只是更新属性,而是整个用户对象。喜欢的东西:

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

或者只是操纵你的对象的ID,如你注意到它的工作:

u = User.create; a = Ad.create; a.user_id = u.id; a.save; a.user_id = 9999; a.save 
0

它看起来像您遇到的问题是因为你试图改变USER_ID直接属性。由于您已将belongs_to :user添加到Ad模型中,因此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

感谢您的详细解释。我发现了第三种解决方法,我认为这是最好的,因为我不想让额外的数据库命中,并更改代码以适应测试套件使用 - 它应该是相反的方式。情况是我有一个关联对象,我想通过JSON将它复制到另一个对象。这在轨道上完美地工作,但Factory Girl(测试夹具生成器)创建加载的对象关联。看下一个答案。 – user681814

+0

你可以让Factory Girl建立对象而不碰到数据库。因此,在你的情况Factory.build(:对象),而不是Factory.create(:对象) –

+0

好吧,没有深入解释Factory.build(:对象)不适合我的目的,我需要的对象在数据库中持久化,但没有加载的关联(build()不会这样做),而纯粹的Factory()将所有东西加载到对象中。看到我的答案做什么 - 无法回答我自己的问题8小时:-) – user681814

相关问题