Search code examples
ruby-on-railsruby-on-rails-3serializationupdate-attributes

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?


Solution

  • 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