Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically updating ActiveRecord object attribute

I want to update an AR object, using an attribute name that I access from another source (in the real data, there are lots).

class DailyScore < ActiveRecord::Base
    attr_accessor :mail, :date, :sun, :express, :average
end

array = ['mail','date','sun']
thing = DailyScore.new

How can I turn each item in the array into a symbol, and update the record accordingly?

Ideally, I'd like something like this:

array.each do |field|
    thing.field = 1.0 #Float
end

UPDATE (with fuller code example):

def sort_and_deliver_scores(dates)

  # dates example: ["2016-01-01","2016-01-12","2016-01-05"]

  dates.each do |dateString|

    params = {}
    params[:date] = dateString
    so_far = 0.0

    ["mail","express","sun"].each do |paper|

      # get all records that correspond to this newspaper
        @dailyscores = MyModel
                       .pluck(:paper,:score, :date)
                       .select{|b| b[0]==paper}

      # add up the scores in all records for each paper that day  

      today = @dailyscores.map{|c| c[1]}.inject(:+)

      todaysScoresByPaper = (today/@dailyscores.size).round(1)
      so_far += todaysScoresByPaper

      params[paper.to_sym] = todaysScoresByPaper    

    end

    params[:average] = (so_far / 3).round(1)
    save_record_as_daily_score_object(params)
  end
end


def save_record_as_daily_score_object(params)
        ds = DailyScore.where(date: params[:date]).first_or_create!
        ds.update!(params)
end

When I check using pry, the params are fine, with Float values stored in the hash, but for some reason, when I save the record, ONLY the :average field gets saved, and all others are nil.

UPDATE 2:

tasks.rake

task :seed_scores => :environment do

         # "select_a_few_dates" sends a few dates as strings
         sort_and_deliver_scores(Score.all.select_a_few_dates)

end

score.rb

def save_record_as_daily_score_object(params)
        ds = DailyScore.where(date: params[:date]).first_or_create!
        ds.update!(params)
        binding.pry

end

PRY output:

[1] pry(main)> ds
=> #<DailyScore:0x0000010add5ff8
 id: 3876,
 mail: nil,
 average: -3.4,
 sun: nil,
 express: nil,
 date: nil,
 created_at: 2016-05-15 09:17:55 UTC,
 updated_at: 2016-05-15 12:45:50 UTC>
[2] pry(main)> params
 => {:date=>"2015-09-02",
 :mail=>-0.6,
 :express=>-0.1,
 :sun=>-3.2,
 :average=>-3.4}

It seems that creating a DailyScore object by dateString is not working, and for some reason, only the :average attribute is being saved.

like image 313
Jonathan_W Avatar asked Aug 27 '26 03:08

Jonathan_W


1 Answers

You can use the ActiveRecord hash interface, which would look like this, using your array variable:

array.each do |field|
  thing[field.to_sym] = "I'll tell you later"
end

thing.save

Alternatively, you can use the params hash appraoch. Rails uses the params hash to perform a similar operation. You can simulate this functionality in this way:

array = ['title','date','height']
values = ['King Lear', '2016-05-13', 13.5]
attributes_hash = {}

array.map {|elem| elem.to_sym }.each_with_index do |field, i|
  attributes_hash[field] = values[i]
end

thing.update(attributes_hash)

The benefit of this approach is that you can update an entire hash of values at one time. If you want to bypass validations and other checks, you can use update_attributes, instead.

For more information, see:

  • ActiveRecord update method
  • ActiveRecord update_attributes
  • Rails update_attributes without save?
like image 192
Michael Gaskill Avatar answered Aug 29 '26 17:08

Michael Gaskill



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!