2012-04-02 18 views
6

Esto es muy similar a Rails partial updates problem with hashes, pero la pregunta realmente no ha sido respondida en mi humilde opinión.Detener ActiveRecord guardar una columna serializada, incluso si no se ha cambiado

El problema es el siguiente: Tengo un modelo con una columna en serie:

class Import < AR::Base 
    serialize :data 

En mi caso, estos datos, y deben, no cambia después de que el primer salvamento/creación del modelo. Por lo tanto, quiero desactivar la característica de AR que siempre guarda columnas serializadas (que normalmente es una buena idea, ya que no puede detectar esos cambios). Quiero desactivar el guardado porque los datos pueden ser bastante grandes y el modelo se actualizará con frecuencia.

Ya he probado monkeypatching en ActiceRecord :: :: AttributeMethods sucios como esto:

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

pero esto parece tener ningún efecto. ¿Alguien tiene una mejor idea?

Esto está bajo rieles 3.0.12

Respuesta

6

Lo que terminé haciendo, a pesar de que en realidad no es una respuesta a la pregunta original, es la siguiente:

class Import < AR::Base 
    belongs_to :storage 

class Storage < AR::Base 
    serialize :data 

... es decir. mover la columna de datos a un modelo propio y asociar eso con el modelo original. Que en realidad es conceptualmente algo más limpio.

+0

Esto realmente tiene mucho sentido, gracias por compartir la información. Creo que voy a seguir el mismo camino, y usar una asociación polimórfica entre el modelo _Storage_ y otros que tienen columnas serializadas de solo lectura –

3

Aquí es una solución fea parche mono:

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 

Tendrá que recordar llamar cache_spam !, o su atributo serializado no se guardarán.

Cuestiones relacionadas