Forum: Ruby on Rails How do I reference eagerly loaded Models in the View?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B09a3f6cdc4797532647d2d264b5df49?d=identicon&s=25 Jodi Showers (jshow)
on 2006-01-06 17:17
[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
A2c85dc5ee81b12e3cc0a6522e8d079d?d=identicon&s=25 Chris Hall (Guest)
on 2006-01-06 18:45
(Received via mailing list)
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:

<table>
  <!--- column names -->
  <%= render :partial => "item", :collection => @work_order_items %>
</table>

in _item.rhtml:

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


--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
B09a3f6cdc4797532647d2d264b5df49?d=identicon&s=25 Jodi Showers (jshow)
on 2006-01-07 02:36
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.

2. 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 Hall 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:
>
> <table>
>   <!--- column names -->
>   <%= render :partial => "item", :collection => @work_order_items %>
> </table>
>
> in _item.rhtml:
>
> <tr>
>   <td><%= item.product_or_service.part_number %></td>
>   <td><%= item.description %></td>
>   <td><%= item.quantity %></td>
>   <td><%= number_to_currency(item.price) %></td>
>   <td><%= number_to_currency(item.sub_total) %></td>
> </tr>
>
>
> --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
This topic is locked and can not be replied to.