Есть ли способ предотвратить обновление сериализованных атрибутов в rails, даже если нет изменений?

Это, вероятно, одна из вещей, которые все новые пользователи узнают о Rails рано или поздно. Я только что понял, что rails обновляет все поля с помощью ключевого слова serialize, не проверяя, действительно ли что-то изменилось внутри. Таким образом, это разумная вещь для общей структуры.

но есть ли способ переопределить это поведение? Если я могу отслеживать, изменились ли значения в сериализованных полях или нет, есть ли способ предотвратить это толкнул инструкцию update? Я попытался использовать "update_attributes" и ограничить хэш интересующими полями, но rails по-прежнему обновляет все сериализованные поля.

предложения?

6 ответов


Да, это меня тоже беспокоило. Это то, что я сделал для Rails 2.3.14 (или ниже):

# 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

тогда в вашем контроллере используйте:

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.

или определите его в своей модели, если вы не ожидаете каких-либо изменений в сериализованном поле после его создания (но update_attribute(:serialized_field => "update me" все еще работает!)

class Model < ActiveRecord::Base
  serialize :serialized_field

  def no_serialize_update
    true
  end

end

вот аналогичное решение для Rails 3.1.3.

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

поместите следующий код в 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

я столкнулся с этой проблемой сегодня и в конечном итоге взломал свой собственный сериализатор вместе с геттером и сеттером. Сначала я переименовал поле в #{column}_raw а затем использовал следующий код в модели (для в моем случае).

require 'json'

...

def media=(media)
  self.media_raw = JSON.dump(media)
end

def media
  JSON.parse(media_raw) if media_raw.present?
end

Теперь частичные обновления отлично работают для меня, и поле обновляется только тогда, когда данные фактически изменены.


проблема с ответом Джориса в том, что он цепляется за alias_method_chain цепь, отключив все цепи, сделанные после (например,update_with_callbacks что объясняет проблемы триггеров, которые не вызываются). Я постараюсь сделать диаграмму, чтобы было легче понять.

вы можете начать с цепи, как это

update -> update_with_foo -> update_with_bar -> update_with_baz

обратите внимание, что update_without_foo указывает на update_with_bar и update_without_bar to update_with_baz

так как вы не можете напрямую изменить update_with_bar в внутренние работы alias_method_chain вы можете попытаться подключиться к цепочке, добавив новую ссылку (bar2) и вызвав update_without_bar, так что:

alias_method_chain :update, :bar2

к сожалению, это даст вам следующие услуги:

update -> update_with_bar2 -> update_with_baz

так update_with_foo ушел!

Итак, зная, что alias_method_chain не позволит вам переопределить _with способы мое решение до сих пор было переопределить update_without_dirty и сделайте выбор атрибута там.


Не совсем решение, но хорошим обходным путем во многих случаях для меня было просто переместить сериализованный столбец(ы) в связанную модель - часто это действительно было хорошо подходит семантически в любом случае.


есть также обсуждения в https://github.com/rails/rails/issues/8328.