ac360
ac360

Reputation: 211

Find_or_initialize_by Issue

I have a DB Table for a Model entitled TradeDailyAverage. It has a date (DateTime) & averageprice (decimal) column

When I run this, I can't get the averageprice attribute to update:

      newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
      newaverage.update_attributes :averageprice => dailyaverage

Further, when I run this, the date will show up, but the averageprice will not show up in rails console. It only shows up as blank:

newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
          puts newaverage.averageprice
          puts newaverage.date

Is there anything special that I need to do to averageprice before I save it?

Here is all of the entire Rake Task for your reference:

averages = Trade.where('date >= ?', 7.days.ago).average(:price, :group => "DATE_TRUNC('day', date - INTERVAL '1 hour')")

    # 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)

      newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
      newaverage.update_attributes :averageprice => dailyaverage

Upvotes: 0

Views: 5070

Answers (2)

ac360
ac360

Reputation: 211

My issue was simply that upon saving to my database, PostgreSQL was changing the hourly time, possibly due to a timezone issue. Thus, all of my instances above were new, and I couldn't update attributes of an existing model. So, I converted my datetime data to dates, changed my date db column to date instead of datetime, and now everything is working smoothly. Yves gives some great info above though which helped me later on.

Upvotes: 1

Yves Senn
Yves Senn

Reputation: 1996

If you want to use find_or_initialize_by you need to think carefully about the implications. Lets take your first example:

  newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
  newaverage.update_attributes :averageprice => dailyaverage

This should work, when the TradeDailyAverage for the given date is already in the database. It should not work however, when you get a new record back. The reason is simply because a new record is not persisted to the database. There is no way for update_attributes to update a non persisted record. You have two options here:

1.) Do not use update_attributes but assign the value and call save. This works for both, new and created records:

  newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
  newaverage.averageprice = dailyaverage
  newaverage.save

2.) Do not use find_or_initialize_by but find_or_create_by. This way if the record does not exist, a new one is directly written to the database. Now update_attributes should work because you always get persisted records back. Note that this approach has the drawback that you save records without an averageprice to the database. I would not recommend that.

The explanation above should also explain your second example:

newaverage = TradeDailyAverage.find_or_initialize_by_date(date)
puts newaverage.averageprice
puts newaverage.date

This should output the averageprice and the date for persisted records. If you get a newly initialized record back though, it will only display the date. Since you only initialized the record with a date object, there is no way that the averageprice is already set.

Upvotes: 3

Related Questions