one_to_many relationship, auto update?

Hi,

I am new to Ruby on Rail. I have a question regarding the one_to_many relationship which doesn't automatically update the relationship by Rails.

Support I have two classes: Car and Wheel.

Car has many Wheel where as Wheel belongs to Car. Therefore, I declare the classes as below:

// car.rb class Car < ActiveRecord::Base         has_many :wheels end

// wheel.rb class Wheel < ActiveRecord::Base         belongs_to :car end

The is how I test drive in rails console:

car1 = Car.new car1.save

car2 = Car.new car2.save

wheel1 = Wheel.new wheel1.save

wheel2 = Wheel.new whee2.save

car1.wheels << wheel1

car1.wheels << wheel2 car2.wheels << wheel2

Note that car1 wheels STILL contains wheel2 when I reassign wheel2 to car2. I expect car1 doesn't have wheel2 anymore...

I don't know why it doesn't get update automatically that car1 shouldn't contains wheel2 anymore. Can anybody help me to solve this problem?

Thanks.

Try fetching car1 from the database again, I suspect it is just that the version in memory does not know that you have removed one of its wheels. I think there is a way of asking it to refresh itself to save explicitly fetching it, but I cannot bring it to mind at the moment.

Colin

Thanks,

It works if I fetch it to the memory again!

Just wonder if there is such a way that it can refresh itself in the memory?

Thanks,

It works if I fetch it to the memory again!

Just wonder if there is such a way that it can refresh itself in the memory?

It is very rarely an issue in a real app. Remember objects typically only have a very short life cycle as they only exist for a single http request. I am not saying that it is never an issue, but only rarely.

Colin

Interesting. What's *supposed* to happen is that wheel's car_id gets set to the id of the car. When you do that the second time, that *should* overwrite the first one, so that the first one's wheels would no longer include it. Why that's not happening, I'm not sure. Just tried it with one of my own apps, that has Decisions that belong to Users. Transcript:

ruby-1.9.3-head :005 > u1 = User.new   => #<User id: nil, other details snipped> ruby-1.9.3-head :006 > u1.id = 1001   => 1001 ruby-1.9.3-head :007 > u2 = User.new   => #<User id: nil, other details snipped> ruby-1.9.3-head :008 > u2.id = 1002   => 1002 ruby-1.9.3-head :009 > d = Decision.new   => #<Decision id: nil, user_id: nil, other details snipped> ruby-1.9.3-head :020 > d.id = 1000 => 1000 ruby-1.9.3-head :010 > u1.decisions << d   => [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :011 > u2.decisions << d   => [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :016 > d => #<Decision id: 1000, user_id: nil, other details snipped> ruby-1.9.3-head :021 > u1.decisions => [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :022 > u2.decisions => [#<Decision id: 1000, user_id: nil, other details snipped>]

How odd, user_id stays nil! Given that, I'm not sure just how Rails "knows" that u#.decisions includes d. If anyone here has a clue, maybe that would clear things up.

Meanwhile, as Colin said, Rails objects usually have a very short lifespan, so maybe the fact that it was in the console, rather than over the course of a bunch of HTTP requests, could have something to do with it. Maybe something gets set when Rails set up or tears down a request object, that isn't happening here. Try making an app in which you can CRUD cars and wheels, put wheels on cars, and take wheels off cars. Let us know what happens when you try to do the same thing through the app.

-Dave

car1.wheels << wheel1

car1.wheels << wheel2 car2.wheels << wheel2

Note that car1 wheels STILL contains wheel2 when I reassign wheel2 to car2. I expect car1 doesn't have wheel2 anymore...

I don't know why it doesn't get update automatically that car1 shouldn't contains wheel2 anymore. Can anybody help me to solve this problem?

Interesting. What's *supposed* to happen is that wheel's car_id gets set to the id of the car. When you do that the second time, that *should* overwrite the first one, so that the first one's wheels would no longer include it. Why that's not happening, I'm not sure. Just tried it with one of my own apps, that has Decisions that belong to Users. Transcript:

ruby-1.9.3-head :005 > u1 = User.new => #<User id: nil, other details snipped> ruby-1.9.3-head :006 > u1.id = 1001 => 1001 ruby-1.9.3-head :007 > u2 = User.new => #<User id: nil, other details snipped> ruby-1.9.3-head :008 > u2.id = 1002 => 1002

It is not appropriate to set the id values manually. Save them instead, then rails will allocate the ids.

ruby-1.9.3-head :009 > d = Decision.new => #<Decision id: nil, user_id: nil, other details snipped> ruby-1.9.3-head :020 > d.id = 1000 => 1000 ruby-1.9.3-head :010 > u1.decisions << d

Again, don't set the ids manually, let rails do it.

=> [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :011 > u2.decisions << d => [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :016 > d => #<Decision id: 1000, user_id: nil, other details snipped> ruby-1.9.3-head :021 > u1.decisions => [#<Decision id: 1000, user_id: nil, other details snipped>] ruby-1.9.3-head :022 > u2.decisions => [#<Decision id: 1000, user_id: nil, other details snipped>]

How odd, user_id stays nil! Given that, I'm not sure just how Rails "knows" that u#.decisions includes d. If anyone here has a clue, maybe that would clear things up.

Try again with letting rails manage the ids and you may get a different result.

Colin

ruby-1.9.3-head :005 > u1 = User.new => #<User id: nil, other details snipped> ruby-1.9.3-head :006 > u1.id = 1001 => 1001 ruby-1.9.3-head :007 > u2 = User.new => #<User id: nil, other details snipped> ruby-1.9.3-head :008 > u2.id = 1002 => 1002

It is not appropriate to set the id values manually. Save them instead, then rails will allocate the ids.

Since it was just for the purpose of a little irb experiment, I didn't want to go to the bother of setting up a new app or jumping through the hoops to set the other fields needed to make the record saveable.

Try again with letting rails manage the ids and you may get a different result.

Essentially my advice to the OP! :slight_smile: Let's see what he says.

-Dave

I am confused by "instead, then rails will allocate the ids."

So do you mean it is improper to do this:

user.Description << d

If it is, can you tell me the proper way to link the association?

Thanks, Kahou

Dave Aronson wrote in post #1034152:

kahou l. wrote in post #1033985:

car1.wheels << wheel2 car2.wheels << wheel2

Note that car1 wheels STILL contains wheel2 when I reassign wheel2 to car2. I expect car1 doesn't have wheel2 anymore...

I don't know why it doesn't get update automatically that car1 shouldn't contains wheel2 anymore. Can anybody help me to solve this problem?

The reason this happens is because Rails does not provide inter-model change notification. Take the line:

car2.wheels << wheel2

Only car2 and wheel2 are involved in this statement. There is no change notification system in Rails to send a message to car1 informing it that anything changed. Therefore car1 only knows about the last state change where it was actually involved. If you want car1 to "wake up" and update it's internal state then you must do that yourself by either fetching it again or forcing car1 to reload it's state from the database:

car1.reload

Using reload is typically not necessary in production code because the next request usually refetches the objects involved anyway. But, it can be useful in rare situations or when using the Rails console for exactly the sort of experimenting that you're doing now.

kahou l. wrote in post #1034157:

I am confused by "instead, then rails will allocate the ids."

So do you mean it is improper to do this:

user.Description << d

No. Colin was referring to statements like in one of the replies to your question:

u1.id = 1001

Don't do this. ActiveRecord wants to manage setting the id (primary key) itself.

P.S. Sorry for my continued misuse of it's vs. its in earlier posts. That damn apostrophe keeps slipping in where it doesn't belong.

Robert Walker wrote in post #1034162:

The reason this happens is because Rails does not provide inter-model change notification. Take the line:

Oh and BTW. If anyone is wondering why not just add a change notification system to ActiveRecord?Well, How far do you want to take that. It might be relatively straightforward if you have only a single instance of an application. However, to support scaling to multiple instances, maybe on several servers, you'd have to provide some sort of distributed change notification. Obviously, this complicates matters extensively. It's very much easier to just go back to the database to discover changes in the underlying state.