Jodi Showers
2006-Jan-06 16:17 UTC
[Rails] How do I reference eagerly loaded Models in the View?
[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 -- Posted via http://www.ruby-forum.com/.
Chris Hall
2006-Jan-06 17:44 UTC
[Rails] How do I reference eagerly loaded Models in the View?
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 On 1/6/06, Jodi Showers <jodi@nnovation.ca> wrote:> > [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 > > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060106/33b9f836/attachment.html
Jodi Showers
2006-Jan-07 01:36 UTC
[Rails] Re: How do I reference eagerly loaded Models in the View?
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 theproducts_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-- Posted via http://www.ruby-forum.com/.