views:

15

answers:

1

Hello all,

In my app, I have three models: Markets, Deals, and Users. Relationships are pretty straightforward:

class Market
  has_many :users
  has_many :deals
end

class User
  belongs_to :market
end

class Deal
  belongs_to :market
end

Now, in order to more easily query some of those relationships, the Market model has some methods attached to it:

class Market
  def revenue_by_date(date)
    deal = self.deals.find(:all, :select => 'id, purchased, price', :conditions => ["start_time =?", date])[0]
    i = 0
    if deal && deal.isMade?
      i = deal.purchased * deal.price
    end
    return i
  end

  def users_for_market
    return self.users.count
  end
end

So, in my controller, I need to collect data pertaining to all of the markets together, and then I need to drill down in various places. So I have a query that looks like this:

markets = Market.find(:all, :order => :name, :include => [:users, :deals])

This runs the two include queries as expected, but later in my code where I do something like this:

markets.each do |m|
  m.users_for_market
  m.revenue_by_date(date_var)
end

it runs the individual queries in those model methods. I thought that the :include was supposed to supplant that?

What can I do to cut down on the number of queries I'm running?

+1  A: 

:include is awesome, but if you call new database queries down the line, it won't stop you. You can reduce the DB hits though, by changing your Market methods:

class Market
  def revenue_by_date(date)
    deal = self.deals.select{|d| d.start_time == date}.first

    i = 0
    if deal && deal.isMade?
      i = deal.purchased * deal.price
    end
    return i
  end

  def users_for_market
    return self.users.size
  end
end

Let's work backward - in users_for_market, the count method does a db lookup, the size method just checks the array. In revenue_by_date, I replaced your database query with a ruby select method, which iterates over the items and only returns the ones where the select block equates to "true".

Rails won't stop you from making more database queries, but you can easily use ruby code to work with what you've already loaded. I hope this helps!

Jaime Bellmyer
The users_for_market change works like a champ. Quick question about the revenue change though: what happens if I haven't already run a find statement? For instance, I'm calling the method as a one off. Since it expects an array, can I just use an if statement if the array doesn't exist?
Kevin Whitaker
NM! Figured out the answer to my own question. Your solution works great!
Kevin Whitaker
Sure, no problem! Yeah, if you haven't called 'find' already, calling market.deals will do that automatically. That's probably what you figured out, I just wanted to mention for anyone else reading this.
Jaime Bellmyer