views:

19

answers:

3

Consider the following models..

class Product < ActiveRecord::Base
  has_many    :pricings
end

class Pricing < ActiveRecord::Base
  belongs_to  :server
end

Pricing is a historical tracking table for prices of products, so there may potentially be hundreds of price points captured over time. What I want to add is a way to get only the current pricing for the product.

While i can add a has_one relation to the model like the following:

has_one  :current_pricing, :class_name => "Pricing", :foreign_key => "product_id",
         :order => 'created_at DESC'

This will fetch me all the Pricings for the current product before returning me only the first record.

I am looking for a way to accomplish this in a more efficient manner and was hoping that someone would have input or previous experience doing something similar.

A: 

Add a :limit to the query?

DGM
Tried it, but you can't add a :limit to a has_one relationship
bdorry
+1  A: 

You could use a named scope. Put this at the top of the Pricing model:

class Pricing < ActiveRecord::Base
  named_scope :current, lambda { |product| { :conditions => { :product_id => product.id }, :order => 'created_at DESC', :limit => 1 } }
end

Then to get the current pricing of a product, you'd call "Pricing.current(product).first".

In addition to the named scope on pricing, you could add an accessor method to the Product model like so:

class Product < ActiveRecord::Base
  def current_pricing
    Pricing.current(self).first
  end
end
Ben Lee
alternatively, you could avoid the named scope and put all the logic in Product#current_pricing, with "Pricing.find(:first, :conditions => { :product_id => self.id }, :order => 'created_at DESC'). A named scope is more versatile since it can be chained with additional finders, but putting all the logic in Product is simpler if that's all you need.
Ben Lee
I should clarify a little bit - I actually have a named scope I omitted on the product class already. I'm looking for a way to do this via an include or join so that I would not have to run separate queries for every item returned. I've done similar in raw SQL before, just having trouble with the ActiveRecord syntax.
bdorry
I don't understand what you mean.
Ben Lee
I needed a way to easily query for the most up to date pricing for a product - what I ended up doing was using the scope you provided and creating an after_save callback on the pricing class that updates a 'current_pricing_id' on the product. Using that id I added an association to the product object, which I can easily include in queries. Plus since updating the id is handled via callbacks the updating is very transparent to the user.
bdorry
Ah, now I understand what you mean. You might be able to accomplish this with complicated join and group finder parameters, but I think your solution of caching the latest pricing id in the product table itself is probably best -- it simplifies the code greatly and is way more efficient database-wise, which makes it scalable (if that is a concern for you).
Ben Lee
Yea, the implementation seemed simpler overall doing it this way. Thanks for the input.
bdorry
+1  A: 

You're looking for a named_scope.Define this in your Pricing model.Supply it with 1 parameter, the product_id.

named_scope :latest_price, lambda { |*args| {:conditions => {:product_id => args.first}, :order => "created_at DESC", :limit => 1} }

Excellent screencast on this by the way.

Shreyas Satish
this is basically the same as the solution i proposed, but your braces are mis-aligned -- the closing :conditions hash brace is in the wrong place
Ben Lee
I should clarify - I am looking to do this via an include or joins so that I limit the amount amount of queries being ran.
bdorry
Oops ! And hey as you can see, the timestamps, as they suggest, our posts went up almost at the same time. Thanks for heads-up !
Shreyas Satish