8

Probablemente esta sea una de las cosas que todos los nuevos usuarios averiguen sobre Rails tarde o temprano. Me acabo de dar cuenta de que los rieles están actualizando todos los campos con la palabra clave serialize, sin verificar si algo realmente cambió dentro. De una manera que es lo sensato para hacer para el marco genérico.¿Hay alguna manera de evitar que los atributos serializados en los rieles se actualicen incluso si no hay cambios?

¿Pero existe una forma de anular este comportamiento? Si puedo hacer un seguimiento de si los valores en un campo serializado han cambiado o no, ¿hay alguna manera de evitar que se inserte en la declaración de actualización? Intenté usar "atributos_de_actualización" y limitar el hash a los campos de interés, pero los raíles todavía actualizan todos los campos serializados.

Sugerencias?

Respuesta

1

Sí, eso también me molestaba. Esto es lo que hice por rieles 2.3.14 (o inferior):

# 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 

A continuación, en el uso del controlador:

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. 

o definir en su modelo si usted no espera ningún cambio en el campo en serie, una vez creado (pero update_attribute (: serialized_field => "me actualizar" todavía funciona)

class Model < ActiveRecord::Base 
    serialize :serialized_field 

    def no_serialize_update 
    true 
    end 

end 
+0

que parece bastante prometedor. ¡Gracias! Ahora que sé qué anular, podría hacer eso (junto con el super) en lugar de usar el método de encadenamiento, a menos que existan advertencias al tomar ese enfoque. Avíseme si ve algún problema si simplemente uso override + super. – Tabrez

+0

Lo intenté también, pero noté que no funcionaba. El problema es que update_with_dirty ya está cargado (a través de la cadena original) antes de que tengas la oportunidad de anularlo y usar super. – Joris

+0

Entendido. Gracias Joris! – Tabrez

2

Aquí es una solución similar para Rails 3.1.3

!.

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

poner el código siguiente en config/inicializadores/

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 
+0

no funciona en rieles 3.2.13 –

+0

Funciona (probado en RoR 3.2.18). ¿Ha añadido: def no_serialize_update cierto final al modelo que contiene el 'serialize' desea que no será informado por defecto? –

1

me encontré con este problema hoy en día y terminó la piratería mi propia serializador junto con un getter y setter. Primero cambié el nombre del campo a #{column}_raw y luego usé el siguiente código en el modelo (para el atributo media en mi caso).

require 'json' 

... 

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

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

Ahora las actualizaciones parciales funcionan bien para mí, y el campo solo se actualiza cuando los datos se cambian realmente.

+0

increíble solución! Me interesaría saber si hay algún inconveniente, tal vez el rendimiento. tener que analizar el crudo cada vez? –

1

El problema con la respuesta de Joris es que se engancha en la cadena alias_method_chain, deshabilitando todas las cadenas hechas después (como update_with_callbacks que explica los problemas de los desencadenadores no llamados). Trataré de hacer un diagrama para que sea más fácil de entender.

Usted puede comenzar con una cadena como este Aviso

update -> update_with_foo -> update_with_bar -> update_with_baz 

que update_without_foo puntos a update_with_bar y update_without_bar a update_with_baz

Puesto que no se puede modificar directamente update_with_bar por el funcionamiento interno de alias_method_chain puede intentar para enganchar en la cadena agregando un nuevo enlace (bar2) y llamando a update_without_bar, entonces:

alias_method_chain :update, :bar2 

Desafortunadamente, esto le ayudará a la siguiente cadena:

update -> update_with_bar2 -> update_with_baz 

Así update_with_foo se ha ido!

Así, sabiendo que alias_method_chain no le permitirá volver a definir métodos _with mi solución hasta el momento ha sido redefinir update_without_dirty y hacer la selección de atributos allí.

1

No fue una solución, pero una buena solución en muchos casos para mí fue simplemente mover la (s) columna (s) serializada (s) a un modelo asociado; a menudo, esto en realidad era una buena forma semántica de todos modos.

Cuestiones relacionadas