How do I reference eagerly loaded Models in the View?


#1

[I hope the repost isnt’ “minded”. Following the advice of another thread I’ve changed the subject to a question. If I haven’t included important info, please ask. My database is being unduly killed. Jodi]

Cheers on-the-Rails-ers,

Before I start, I’ve read the ActiveRecord docs on eager loading, but
for the life of me, I can’t seem to get it working.

What I mean by this, is that the eager query itself looks good (I can
paste the
sql dump, but it looks good to me - and runs well independantly), but it
seems that I’m referencing the eagerly loaded data in a way that
activerecord doesn’t understand (my assumption) - thus I’m getting a
tonne of additional queries.

[first, the domain:
WorkOrders has_many: WorkOrderItems
&&
WorkOrderItems belongs_to :WorkOrder
WorkOrderItems belongs_to :ProductsAndServices

secondly, the controller looks like:
@work_order = WorkOrder.find(params[:id])
@work_order_items = @work_order.WorkOrderItems.find(:all, :include
=> [:WorkOrder, :ProductsAndServices ] )

(you’ll note that I’m eagerly loading the work_order from the
work_order_item - that’s because the WorkOrderItem model references its
work_order for some processing - see “pst” below)

And in the view, I reference as such:

@work_order_items.each {|item| @work_order_items_table.data <<
Hash[“Part #”, item.ProductsAndServices.part_number,“Description”,
item.description, “Quantity”,item.quantity, “Price”,
number_to_currency(item.price), “Extended”,
number_to_currency(item.sub_total)]}

The above generates @work_order_items.count queries to the
ProductsAndServices table. Why is that? Do I reference the eagerly
loaded data incorrectly?

With the same symptom, further down in the same view I make a call to a
WorkOrderItems method “pst” - this method references both eagerly
referenced models - WorkOrders and ProductsAndServices using the syntax:

#calculate the line item pst
def pst
if (self.WorkOrder.charge_pst? and
self.ProductsAndServices.charge_pst?)
sub_total * 0.07
else
0.0
end
end

Again, I see that database queries are made to retrieve both WorkOrder
and ProductsAndServices.

Probably the same problem on my part. Any ideas ninjas?

Thanx for any help.

Jodi


#2

your use of camelcase is confusing to read. the convention in ruby is
to
not use camelcase for anything other than class names… and it could
possible be the cause of your problem.

i’ve taken the liberty to rename/organize some things, hope you don’t
mind.

table name : work_orders

class WorkOrder < AR::Base
has_many :work_order_items
end

table name : work_order_items

class WorkOrderItem < AR::Base
belongs_to :work_order
belongs_to :product_or_service
end

table name : product_or_services

class ProductOrService < AR::Base
has_many :work_order_items
end

—quote—
secondly, the controller looks like:
@work_order = WorkOrder.find(params[:id])
@work_order_items = @work_order.WorkOrderItems.find(:all, :include =>
[:WorkOrder, :ProductsAndServices ] )
—end quote—

instead, why not do:

@work_order_items = WorkOrderItem.find(:all, :conditions => [
“work_order_id
= ?”, @params[:id] ], :include => [ :work_order, :product_or_service ])

—quote—
@work_order_items.each {|item| @work_order_items_table.data <<
Hash[“Part #”, item.ProductsAndServices.part_number,
“Description”, item.description,
“Quantity”,item.quantity,
“Price”, number_to_currency(item.price),
“Extended”, number_to_currency(item.sub_total)]}
—end quote—

not sure what the purpose of this is…i’m assuming this is html table
data? why not just use a partial?

fi that’s the case, then in list view:

<%= render :partial => "item", :collection => @work_order_items %>

in _item.rhtml:

<%= item.product_or_service.part_number %> <%= item.description %> <%= item.quantity %> <%= number_to_currency(item.price) %> <%= number_to_currency(item.sub_total) %>

–quote—
#calculate the line item pst
def pst
if (self.WorkOrder.charge_pst? and
self.ProductsAndServices .charge_pst?)
sub_total * 0.07
else
0.0
end
end
—end quote—

again, using camelcase…try

def pst
(work_order.charge_pst? and product_or_service.charge_pst?) ?
(sub_total *
0.07) : 0.0
end

hope this helps


#3

Chris - I’ve just finally worked through your suggestions.

While they didn’t directly lead to an answer, it did serve to help me
re-examine the problem points (plus standardize my ruby syntax (cat java

/dev/null!).

To your benefit chris, you didn’t have all the information needed to
come up with the solution.

I include the following, for those who can benefit. #1 is a watch-out
(well for me anyhow), and #2 is a cool (documented)feature I discovered.

I’ve since found out the following 2 things.

  1. That recursive(?) model references maybe not be handled well by AR.

@work_order.pst_total is a method, that looks something like:

def pst_total
work_order_items.inject(0) { |sum, item| sum + item.gst_charge }
end

and work_order_item.gst looks like

#calculate the line item gst
def gst
if (work_order.charge_gst? and products_and_services.charge_gst?)
sub_total * 0.08
else
0.0
end
end

As you can see that work_order.gst, calls work_order_item.gst, which
then calls work_order.charge_gst? (oh what a tangled web we weave!)

Anywho, the end result was work_order_items.count “select * from
work_orders where id = ?”.

I moved the charge_gst logic, and low and behold, lots of work_order
queries are toast.

  1. from above, you can see

    if (work_order.charge_gst? and products_and_services.charge_gst?)

products_and_services is a secondary relationship to work_orders
[work_orders has_many :work_order_items. And :work_order_items
belongs_to :products_and_services].

From the work_order class defn, I’m saving myself some queries on the
products_and_services model using the following notation:

class WorkOrder < ActiveRecord::Base
has_many :work_order_items, :include => [:products_and_services]

The :include option, from the docs
“:include - specify second-order associations that should be eager
loaded when the collection is loaded.”

Thus when I WorkOrder.find(), I not only get associated
work_order_items, but their related products_and_services.

sweet!

Jodi

Chris H. wrote:

your use of camelcase is confusing to read. the convention in ruby is
to
not use camelcase for anything other than class names… and it could
possible be the cause of your problem.

i’ve taken the liberty to rename/organize some things, hope you don’t
mind.

table name : work_orders

class WorkOrder < AR::Base
has_many :work_order_items
end

table name : work_order_items

class WorkOrderItem < AR::Base
belongs_to :work_order
belongs_to :product_or_service
end

table name : product_or_services

class ProductOrService < AR::Base
has_many :work_order_items
end

—quote—
secondly, the controller looks like:
@work_order = WorkOrder.find(params[:id])
@work_order_items = @work_order.WorkOrderItems.find(:all, :include =>
[:WorkOrder, :ProductsAndServices ] )
—end quote—

instead, why not do:

@work_order_items = WorkOrderItem.find(:all, :conditions => [
“work_order_id
= ?”, @params[:id] ], :include => [ :work_order, :product_or_service ])

—quote—
@work_order_items.each {|item| @work_order_items_table.data <<
Hash[“Part #”, item.ProductsAndServices.part_number,
“Description”, item.description,
“Quantity”,item.quantity,
“Price”, number_to_currency(item.price),
“Extended”, number_to_currency(item.sub_total)]}
—end quote—

not sure what the purpose of this is…i’m assuming this is html table
data? why not just use a partial?

fi that’s the case, then in list view:

<%= render :partial => "item", :collection => @work_order_items %>

in _item.rhtml:

<%= item.product_or_service.part_number %> <%= item.description %> <%= item.quantity %> <%= number_to_currency(item.price) %> <%= number_to_currency(item.sub_total) %>

–quote—
#calculate the line item pst
def pst
if (self.WorkOrder.charge_pst? and
self.ProductsAndServices .charge_pst?)
sub_total * 0.07
else
0.0
end
end
—end quote—

again, using camelcase…try

def pst
(work_order.charge_pst? and product_or_service.charge_pst?) ?
(sub_total *
0.07) : 0.0
end

hope this helps