问题
I have the following semi-advanced DB query that is going through hourly prices for the last 10 years and returning daily average prices for the last seven days:
averages = Trade.where('date >= ?', 7.days.ago).average(:price, :group => "DATE_TRUNC('day', date - INTERVAL '1 hour')")
This returns the date (for that day) and an averageprice like this:
"2012-12-29 00:00:00"=>#<BigDecimal:7f97932be328,'0.2513458333 33333333E2',27(27)>
I then loop through each response and save them as new records in a TradeDailyAverage Model.
# Loops through each daily average produced above
averages.each do |date, avg|
# Converts the BigDecimal to Floating Point(?)
averagefloat = avg.to_f
# Rounds the Daily Average to only two decimal points
dailyaverage = number_with_precision(averagefloat, :precision => 2)
# Creates a new Object in the PpDailyAverage table
TradeDailyAverage.create(date: date, averageprice: dailyaverage)
This works, but since this will be an hourly Rake Task, with new prices coming in every hour, how can I change this to first find a TradeDailyAverage by date and if it exists, update the averageprice attribute, or create a new record if it doesn't exist.
Validate_uniqueness is set on TradeDailyAverage Model.
Update
When I do this, 7 items appear, with accurate averages. But they just won't save. When I add newaverage.save! I get a "Validation Error: Date has already been taken!"
newaverage = TradeDailyAverage.find_or_initialize_by_date(date: date)
newaverage.averageprice = dailyaverage
puts newaverage.date
puts newaverage.averageprice
Also, if I do newaverage.new_record? Ever average returns TRUE
回答1:
I think you'd want something like this:
tda = TradeDailyAverage.first_or_initialize(date: date)
tda.averageprice = dailyaverage
tda.save
回答2:
The issue (thanks to Alex's help) was due to a difference in the datetimes. Upon saving to a PG db, the hour would change on the datetime. This may be due to a timezone issue in the db. So, the code above could not find existing records, since it contained a different hourly time than what has been saved in the database.
Since I am producing daily averages, I don't need the time, just the date in the date column. So, I converted my dates to date to avoid the time difference issue. Also I changed the code a bit with a Case, so that I can report errors. I don't think this is very efficient, but it is working for now. I believe Alex's solution above may also work as long as the datetime values are converted to dates with .to_date:
# Loops through each daily average produced above
averages.each do |datetime, avg|
# Converts the BigDecimal to Floating Point(?)
avgfloat = avg.to_f
# Rounds the Daily Average to only two decimal points
avgprice = number_with_precision(avgfloat, :precision => 2)
# Converts datetime to date since this will not work with datetime (PostgreSQL is time-zoned challenged)
avgdate = datetime.to_date
# These are printed to use for testing.
puts avgdate
puts avgprice
# Starts Case to either report an error, update an existing record, or create new.
case
when avgdate.blank? || avgprice.blank?
puts "Something went terribly wrong with your seven day averages producing algorithm."
when TradeDailyAverage.exists?(:date=>avgdate)
updateavg = TradeDailyAverage.find_by_date(avgdate)
updateavg.averageprice = avgprice
updateavg.save
else
TradeDailyAverage.create(:date=>avgdate, :averageprice=>avgprice)
end # Ends Case
end # Ends Loop for each Daily Average
来源:https://stackoverflow.com/questions/14162771/model-attribute-wont-update-validation-error-called-on-separate-attribute-not