My question is best illustrated by an example. Let’s say we have two
simple classes, Order and LineItem defined as follows.
class Order < ActiveRecord::Base
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
end
We then open up script/console and load our first order and compare the
object IDs of the order that is returned and the order on the first line
item. These values never seem to be the same no matter how I obtain the
line item.
o = Order.find(:first)
l = o.line_items.first
o.object_id == l.order.object_id
=> false
The IDs are also not the same if you do this…
o = Order.find(:first, :include => :line_items)
l = o.line_items.first
o.object_id == l.order.object_id
=> false
Or this…
o = Order.find(:first, :include => :line_items)
l = o.line_items.select{ |l| true }.first
o.object_id == l.order.object_id
=> false
I noticed this because I have a method in my LineItem class that
attempts to update a value on it’s parent Order object like so:
def line_total=(value)
self.line_total = value
self.order.total += self.line_total
end
The parent Order’s value is never updated because it is a different
object instance than the Order that the line item has a reference to.
Am I doing something wrong or is this always the case in Rails? Any
help would be much appreciated.
o.object_id == l.order.object_id
The parent Order’s value is never updated because it is a different
object instance than the Order that the line item has a reference to.
Am I doing something wrong or is this always the case in Rails? Any
help would be much appreciated.
Regards,
Jason
They’re different ruby objects. But you should be able to do: @order
== @line_item.order, since that checks the #id of both records.
In your LineItem class, you’ll have to save the order to the database
and reload any instances you have in memory to see the changes
reflected.
… the line item would have a reference to the same (Ruby) order
object in memory since I’m including the line items in the load AND
using an Array method to select the object. It seems wasteful to have
the line item keep it’s own copy of the order object in memory and makes
it difficult to encapsulate functionality cleanly.
I just played around with this. It’s even worse than that.
After these two lines
o = Order.find(:first, :include => :line_items)
l = o.line_items.select{ |l| true }.first
the l object does not have any order attribute at all. In the third
line,
o.object_id == l.order.object_id
l.order actually loads a new order object with a new SQL query.
In your LineItem class, you’ll have to save the order to the database
and reload any instances you have in memory to see the changes
reflected.
Any idea why it is implemented this way? At the very least I would
think that under this scenario…
o = Order.find(:first, :include => :line_items)
l = o.line_items.select{ |l| true }.first
o.object_id == l.order.object_id
=> false
… the line item would have a reference to the same (Ruby) order
object in memory since I’m including the line items in the load AND
using an Array method to select the object. It seems wasteful to have
the line item keep it’s own copy of the order object in memory and makes
it difficult to encapsulate functionality cleanly.
I don’t mean to shoot the messenger with this post. I’m just wondering
if you (or anyone else) could shed some light on the implementation
motivations here.
Two “equal” orders, but very different because one has the line_items
pre-ordered.
In your case you can do this:
o = Order.find(:first, :include => {:line_items => :order})
But, that generates an SQL query of WTF proportions.
I wrote about this in more depth on my blog, going into the query
caching stuff in Edge Rails, and an active_record_context plugin I
wrote. They’re both very simple implementations of an identity system
that help out in most of the common cases: