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.