I have two models in my Rails application which keeps track of the prices of products at different shops. Here they are, but simplified:
class Product < ActiveRecord::Base
attr_accessor :name
def latest_prices
prices.where('created_at >= ?', 30.days.ago)
end
def average_price
latest_prices.prices.map(&:value).sum / latest_prices.count
end
end
class Price < ActiveRecord::Base
attr_accessor :value, :shop_name, :created_at
belongs_to :product
end
I now want to find all Price objects which fall below the current average for that product. That basically means all Prices created in the last 30 days, that have a price below the recent average price for that Product.
Is this possible? I'm using Postgres.
Edit: I should have mentioned - I want to implement this method from the Price model - that is, just be able to display all prices that are a good deal, rather than all prices for a Product that are good deals.
Thanks in advance for any help!
Using named scopes in ActiveRecord, you can use composition to get what you want:
class Product < ActiveRecord::Base
attr_accessor :name
has_many :prices
end
class Price < ActiveRecord::Base
attr_accessor :value, :shop_name, :created_at
belongs_to :product
scope :latest, where('created_at >= ?', 30.days.ago)
scope :less_than, lambda { |value| where("value < ?", value) }
def good_deals
latest.less_than(average('value'))
end
end
try this:
class Product < ActiveRecord::Base
attr_accessor :name
def your_query
prices.where('created_at >= ?', 30.days.ago).where('value < ?', average_price)
end
def latest_prices
prices.where('created_at >= ?', 30.days.ago)
end
def average_price
latest_prices.prices.map(&:value).sum / latest_prices.count
end
end
Related
There have 2 tables: Orders and Arrivals. There can be many arrivals on an order. I want to validate the creation of arrivals for a specific order.
Orders has fields book_id and quantity:integer
Arrivals has fields order:belongs_to and quantity:integer
Order.rb:
class Order < ActiveRecord::Base
has_many :arrivals
def total_arrival_quantity
arrivals.map(&:quantity).sum
end
def order_quantity_minus_arrival_quantity
quantity - total_arrival_quantity
end
end
Arrival.rb:
class Arrival < ActiveRecord::Base
belongs_to :order
validates :total_arrival_quantity_less_or_equal_to_order_quantity, on: create
validates :current_arrival_quantity_less_or_equal_to_order_quantity, on: create
def current_arrival_quantity_less_or_equal_to_order_quantity
self.quantity <= order.quantity
end
end
How can I make the two validations work?
Something like this should work,
validate :order_quantity, on: :create
private
def order_quantity
if quantity > order.order_quantity_minus_arrival_quantity
errors.add(:quantity, 'cannot be greater than ordered quantity.')
end
end
I have 3 relevant models in a Rails 4 app - Charge:
class Charge < ActiveRecord::Base
belongs_to :rate
belongs_to :shift
def total
self.rate.value * self.quantity
end
end
Rate:
class Rate < ActiveRecord::Base
has_many :charges
end
and Shift:
class Shift < ActiveRecord::Base
has_many :charges
def total_charge
self.charges.sum('total')
end
end
I'm attempting to use shift.total_charge in my view, but I'm getting the error:
SQLite3::SQLException: no such column: total: SELECT SUM(total) FROM "charges" WHERE "charges"."shift_id" = ?
So it seems that it isn't possible to define total in the Charge model in this way, and have it accessible to sum from the Shift model as an actual column would be. I'm struggling to find the appropriate Rails 4 way of doing this - is it possible to do this in the model, or do I need to create a controller for Charge and try to do the calculation there?
sum works only with columns. You could use something like
class Shift < ActiveRecord::Base
has_many :charges
def total_charge
self.charges.map {|c| c.rate.value * c.quantity }.sum
end
end
and to avoid n+1 problem include Rate in Charge
class Charge < ActiveRecord::Base
belongs_to :rate
belongs_to :shift
default_scope {includes :rate}
end
this is what I write, which is working well with show only the post has been posted in 1 hour,
def feed
Micropost.where("created_at >= ?", Time.zone.now - 10.minutes)
end
my micropost model:
class Micropost < ActiveRecord::Base
has_many :votes, as: :voteable
...
def total_votes
self.up_votes - self.down_votes
end
def up_votes
self.votes.where(vote: true).size
end
def down_votes
self.votes.where(vote: false).size
end
end
my vote model
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :voteable, polymorphic: true
validates_uniqueness_of :user, scope: [:voteable_id, :voteable_type]
end
Related Link:Render posts both by time desc and total_votes
I don't like this solution since it requires multiple hits against your database but with small numbers (hundreds or even thousands) of items it should work just fine.
def feed
posts = Micropost.where("votes>=5 and created_at >= ?", 1.hour.ago)
return posts.select { |post| post.total_votes > 5 }
end
I have the following two models:
class Shelf < ActiveRecord::Model
has_many :wines
def self.order_by_oldest_bottle_of_wine
#TODO: order by the oldest wine bottle...
end
end
class Wine < ActiveRecord::Model
belongs_to :shelf
attr_accessible :produce_date
end
In the shelf model, I want to order shelfs by the oldest wine bottle on the shelf (i.e. shelf with the oldest wine bottle first), but not 100% sure of the implementation.
Many Thanks,
You could do this through a named scope
In your Shelf model you could defined it like so:
named_scope :order_by_oldest_bottle_of_wine, joins: :wines, order: "wines.produce_date DESC"
If you are using Rails 3.x you can use any one of the following
Solution 1:
def self.order_by_oldest_bottle_of_wine
self.wines.order("produce_date DESC")
end
Solution 2: If you want to use scope
class Wine < ActiveRecord::Model
belongs_to :shelf
scope :ordered_by_produce_date, order("produce_date DESC")
attr_accessible :produce_date
end
class Shelf < ActiveRecord::Model
has_many :wines
def self.order_by_oldest_bottle_of_wine
self.wines.ordered_by_produce_date
end
end
def self.order_by_oldest_bottle_of_wine
self.wines.find(:all, :order=>"produce_date DESC")
end
Here's what I have:
module EventDependencyProperties
def start_date
shows.order('show_date ASC').first.show_date
end
def end_date
shows.order('show_date DESC').first.show_date
end
def is_future_show?
end_date >= Date.today
end
end
class Event < ActiveRecord::Base
include EventDependencyProperties
has_many :shows
has_and_belongs_to_many :users
end
class Show < ActiveRecord::Base
belongs_to :event
end
I have bits of code elsewhere using the is_future_show? method. What I would like to do is have a method in the module mixin to return "future shows" using a query that has the same criteria as the is_future_show? method. How would I go about achieving this? I'm very much a newbie to Rails but tainted by knowledge of other languages and frameworks.
Cheers,
Dany.
You can put the query into a scope:
class Show < ActiveRecord::Base
scope :future, lambda { where("show_date > ?", Date.today) }
end
Call it like this:
my_event.shows.future
Edit: Ah I see. To return all events with a show in the future:
Event.joins(:shows).where("shows.show_date > ?", Date.today)
agains this can be scoped:
class Event
scope :future, lambda { joins(:shows).where("shows.show_date > ?", Date.today) }
end
On a side note, I'm not sure about the setup of your models, especially the use of the mixin. Here's what I do:
class Show < ActiveRecord::Base
belongs_to :event
# use default_scope so shows are ordered by date by default
default_scope order("show_date ASC")
end
class Event < ActiveRecord::Base
has_many :shows
has_and_belongs_to_many :users
scope :future, lambda { joins(:shows).where("shows.show_date > ?", Date.today) }
def start_date
shows.first.show_date
end
def end_date
shows.last.show_date
end
def ends_in_future?
end_date > Date.today
end
end
also it would be cleaner if the show_date column for the Show model was just called date (so you could just write show.date rather that show.show_date).