Остановите ActiveRecord, сохраняя сериализованный столбец, даже если он не был изменен - PullRequest
6 голосов
/ 02 апреля 2012

Это очень похоже на Проблема частичного обновления Rails с хешами , но на этот вопрос действительно не было ответа ИМХО.

Проблема заключается в следующем: у меня есть модель с сериализованным столбцом:

class Import < AR::Base
  serialize :data

В моем случае эти данные не изменятся и не должны изменяться после первого сохранения / создания модели.Поэтому я хочу отключить функцию AR, которая всегда сохраняет сериализованные столбцы (что обычно является хорошей идеей, поскольку не может обнаружить эти изменения).Я хочу отключить сохранение, потому что данные могут быть довольно большими, и модель будет часто обновляться.

Я уже пробовал monkeypatching в ActiceRecord :: AttributeMethods :: Dirty, как это:

class Import
  def update(*)
    if partial_updates?
      super(changed | (attributes.keys & (self.class.serialized_attributes.keys - ["data"])))
    else
     super
   end
 end

но это, похоже, не имеет никакого эффекта.У кого-нибудь есть идея получше?

Это под Rails 3.0.12

Ответы [ 2 ]

6 голосов
/ 24 апреля 2012

То, что я в итоге сделал, хотя это не совсем ответ на первоначальный вопрос, это следующее:

class Import < AR::Base
  belongs_to :storage

class Storage < AR::Base
  serialize :data

... т.е. переместить столбец данных в собственную модель и связать ее с исходной моделью. Что на самом деле концептуально несколько чище.

3 голосов
/ 25 октября 2012

Вот уродливое решение для патчей обезьяны:

module ActiveRecord
  module AttributeMethods
    module Dirty
      def update(*)
        if partial_updates?
          # Serialized attributes should always be written in case they've been
          # changed in place.  Unless it is 'spam', which is expensive to calculate.
          super(changed | (attributes.keys & self.class.serialized_attributes.keys - ['spam']))
        else
          super
        end
      end
      private :update
    end
  end
end
class Foo < ActiveRecord::Base
  serialize :bar
  serialize :spam


  def calculate_spam
    # really expensive code
  end

  def cache_spam!
    calculated_spam = calculate_spam
    @changed_attributes['spam'] = [spam, calculated_spam]
    self.update_attribute(:spam, calculated_spam)
  end
end

Вы должны будете не забыть вызвать cache_spam !, иначе ваш сериализованный атрибут никогда не будет сохранен.

...