2

This might be a noob question. But I couldn't figure it out by myself.

In my application' I'm using Rails 3.2.8 with Mongoid and MongoDB.

I have a instance variable like this:

ProgramsController < ApplicationController

@program = Program.find(params[:id])

In my view file, I need to use it many many times. For example;

@program.title
@program.content
@program.schedules (associated with Schedule model)
@program.articles (associated with Article model)

etc.

The issue is, I need to use that instance variable at DIFFERENT parts of the view. So it's not something like doing a @program.each do |t| ....

BUT when ever I use @program at a different part of the view, that means a NEW query each time...

At the moment I have 31 queries for my view. Isn't it too much?

So what is the best practice? How should I use instance variables effectively?

Thanks in advance.

Community
  • 1
  • 1
scaryguy
  • 7,720
  • 3
  • 36
  • 52

3 Answers3

0

I'm not particularly familiar with Ruby/Railys but generally in an MVC framework the fetching of data is handled by the controller so it would only happen once. Subsequent usage of it in a view would be using a single copy cached in memory for the request. Referring to the title in one section of your view and the content in another should not be a performance issue. They would just be property accessors on the cached object.

To verify this, you could time or mock the request to see if it's actually be executed multiple times (I doubt it).

0

Once the item has been stored in @program, it is indeed saved there and subsequent requests will not generate queries. If you only had references to @program.title and @program.content, you would probably be fine. Your issue is from the associations, like @program.schedules and @program.articles. The problem is that the schedules and articles are not being loaded from your @program = Program.find(params[:id]). This is referred to as the n+1 problem. The official docs state, under Eager Loading, that Eager loaded is supported on all relations with the exception of polymorphic belongs_to associations. and In order for eager loading to work, the Identity Map must be enabled.. If these two conditions are true, you should see a reduction in your queries. If you cannot accomodate those requirements, a good solution might be to do somethign like this in the controller, to grab them all ahead of time, in one query: @schedules = Schedule.find_by(program_id: @program.id)

Brad Werth
  • 17,411
  • 10
  • 63
  • 88
  • I've tried the last approach you've mentioned and my queries reduced to 23 now. But isn't it still too much? :| Any other solutions you might recommend? – scaryguy Nov 18 '12 at 16:36
  • Basically the same thing, but expanded. Every time you see a dot (`.`), check if it's an attribute or a relationship - if it's a relationship, prefetch it. But it may be better to try to get the eager loading working. – Brad Werth Nov 18 '12 at 16:54
  • Hey! I think I made Eager Loading. I use Mini Profiler and I see something interesting. Before eager loading, Executing action: had 2 sql. Rendering: seciton had 17 sql. Now it's reverse, Executing action: 17 and Rendering: is 2. Aren't those same? :| – scaryguy Nov 18 '12 at 17:23
  • Yeah, it would, in this case, appear so. At least it's moving closer to the right spot. It may be that there are still opportunities to reduce the number of queries, depending on how your controller logic is set up... – Brad Werth Nov 18 '12 at 18:42
0

I would rather use ActiveRecord, if possible.

Rails Official doc says : You can use the :include option to specify second-order associations that should be eager-loaded when this association is used.

Here schedule and article are first-order associations. So you wouldn't need it if you were using ActiveRecord.

Official doc says also :

There's no need to use :include for immediate associations – that is, if you have Order belongs_to :customer , then the customer is eager-loaded automatically when it’s needed.

So you would probably won't bother this issue if you were using ActiveRecord.

Douglas
  • 5,229
  • 3
  • 43
  • 54