在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
在app / models / Ad.rb中添加关联
Class User < ActiveRecord::Base
belongs_to :user
end
现在到了有趣的部分。我想直接更改对象上的外键。我使用 rails c
进入rails控制台$ u = User.create
$ a=Ad.create(:user=>u)
$ a.update_attributes(:user_id => 9999)
$ a.user_id
=> 4
所以这不起作用。我尝试直接设置对象,而不是将其传递给create:
$ 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 :(得分: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
答案 1 :(得分:0)
您遇到的问题似乎是因为您尝试直接更改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
对象比后端外键更重要。
答案 2 :(得分: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
您可以让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
}