Rails3 activerecord update_attributes无法保存foreign_key

时间:2011-07-08 15:03:10

标签: ruby-on-rails database activerecord

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

有效。但是,改变对象上的外键似乎还有很长的路要走。它还需要额外的数据库命中,如果一个对象内部需要更新自己,最后它是相当混乱的代码。怎么办?

3 个答案:

答案 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
}