Search code examples
ruby-on-railsrubyruby-on-rails-5

Child attributes updated with .first_or_initialize not saving - Ruby on Rails


I have a quote which has many shipping methods. When I save the quote, the shipping method prices are not updated when I use .first_or_initialize. I have also tried setting autosave: true on my association like so:

quote.rb

has_many :shipping_methods, dependent: :destroy, autosave: true

I have a callback which refreshes my shipping methods & their prices (quote.rb).

before_save :refresh_shipping_rates

def refresh_shipping_rates
  ShippingMethod.refresh_rates(self, admin = true)
end

My ShippingMethod.refresh_rates function (shipping_method.rb):

def refresh_rates(record, admin)
    #api call that returns a list of rates
    updated_rates.each do |i|
         rate = record.shipping_methods.where(name: i.name).first_or_initialize
         rate.price = i.price
    end
end

My quote is updated via a form submission to my update action.

def update
    @quote = Quote.find(params[:id])
    @quote.update(parameters)

    if @quote.valid?

        flash[:notice] = []
        flash[:notice] << "Successfully Updated"
        render "update"
    else
        flash[:error] = []
        @quote.errors.full_messages.each do |message|
            flash[:error] << message 
        end
        render "layouts/fail"
    end

end

I assume the shipping_methods should be updated simply because they are associated to the parent record that is being updated even though none of their attributes are submitted through the form.


Solution

  • When you call

     rate = record.shipping_methods.where(name: i.name).first_or_initialize
    

    shipping_method is loaded from DB to a new object in memory that is not associated with quote. You should either:

    1. update the objects that are loaded in shipping_method association (without calling any methods that fetches them from the DB), or
    2. save shipping methods separately

    I would advise to try 1).