开发者

Rails3 activerecord update_attributes can't save foreign_key

In rails 3.0.9/ruby 1.9.2 I'm getting unexpected behaviour when i try to update model foreign keys.

I start with a fresh install

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

Add association in app/models/Ad.rb

Class User < ActiveRecord::Base
  belongs_to :user
end

Now to the fun part. I want to change the foreign key on an object directly. I enter rails console with rails c

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

So that doesn't work. I try to set the object directly instead of passing it to create:

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

Doesn't work.

The only thing that works is this:

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

Can someone please explain what is going on, and how can I change the foreign keys of my objects? I assume the "blocking" behaviour happens when the objects associations are instantiated and there is a conflict, so one answer is to do this:

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

That works. But it seems like a long way to go just for changing a foreign key on an object. It also takes an extra DB hit, and lastly it is quite messy code if an object internally needs to update itself. What to do?


It looks like the issue you're experiencing is because you're trying to change the user_id attribute directly. Because you've added belongs_to :user to the Ad model, Rails has a number of additional methods to help deal with the association. Here's the page from the rails guides on the belongs_to methods: http://edgeguides.rubyonrails.org/association_basics.html#belongs_to-association-reference

In this case you can use something like this:

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

a.user_id will equal u1.id

To update the association, you can do this:

a.user = u2
a.save!

and now a.user_id should equal u2.id

Rails is doing this because it assumes that the association is more than just a foreign key as the User object is more important than the backend foreign key.


Well, I think the proper explanation requires me to check the source code of Rails and check what it is happening with update_attributes when you assign an object to the relationship. I will check that later.

But I guess the correct thing to do is not just update the attribute, but the whole user object. Something like:

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

Or just manipulate the ids of your object like you noticed it worked:

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


Just unload the loaded object before doing the update

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

You can make Factory girl create objects with unloaded associations like this

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜