Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to prevent serialized attributes in rails from getting updated even if there are not changes?

This is probably one of the things that all new users find out about Rails sooner or later. I just realized that rails is updating all fields with the serialize keyword, without checking if anything really changed inside. In a way that is the sensible thing to do for the generic framework.

But is there a way to override this behavior? If I can keep track of whether the values in a serialized fields have changed or not, is there a way to prevent it from being pushed in the update statement? I tried using "update_attributes" and limiting the hash to the fields of interest, but rails still updates all the serialized fields.

Suggestions?

like image 755
Tabrez Avatar asked Jan 05 '12 15:01

Tabrez


2 Answers

Here is a similar solution for Rails 3.1.3.

From: https://sites.google.com/site/wangsnotes/ruby/ror/z00---topics/fail-to-partial-update-with-serialized-data

Put the following code in config/initializers/

ActiveRecord::Base.class_eval do
  class_attribute :no_serialize_update
  self.no_serialize_update = false
end

ActiveRecord::AttributeMethods::Dirty.class_eval do
  def update(*)
    if partial_updates?
      if self.no_serialize_update
        super(changed)
      else
        super(changed | (attributes.keys & self.class.serialized_attributes.keys))
      end
    else
      super
    end
  end
end
like image 64
Sibevin Wang Avatar answered Oct 31 '22 14:10

Sibevin Wang


Yes, that was bugging me too. This is what I did for Rails 2.3.14 (or lower):

# config/initializers/nopupdateserialize.rb

module ActiveRecord
  class Base
    class_attribute :no_serialize_update
    self.no_serialize_update = false
  end
end

module ActiveRecord2
  module Dirty

    def self.included(receiver)
      receiver.alias_method_chain :update, :dirty2
    end

    private 

    def update_with_dirty2
      if partial_updates?
        if self.no_serialize_update
          update_without_dirty(changed)
        else
          update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
        end
      else
        update_without_dirty
      end
    end

  end
end

ActiveRecord::Base.send :include, ActiveRecord2::Dirty

Then in your controller use:

model_item.no_serialize_update = true
model_item.update_attributes(params[:model_item])
model_item.increment!(:hits)
model_item.update_attribute(:nonserializedfield => "update me")

etc.

Or define it in your model if you do not expect any changes to the serialized field once created (but update_attribute(:serialized_field => "update me" still works!)

class Model < ActiveRecord::Base
  serialize :serialized_field

  def no_serialize_update
    true
  end

end
like image 1
Joris Avatar answered Oct 31 '22 15:10

Joris