8

Questa è probabilmente una delle cose che tutti i nuovi utenti scoprono su Rails prima o poi. Ho appena realizzato che i binari stanno aggiornando tutti i campi con la parola chiave serialize, senza verificare se qualcosa è veramente cambiato all'interno. In un modo che è la cosa sensata da fare per il quadro generale.C'è un modo per impedire che gli attributi serializzati nei binari vengano aggiornati anche se non ci sono cambiamenti?

Ma esiste un modo per ignorare questo comportamento? Se posso tenere traccia del fatto che i valori di un campo serializzato siano cambiati o meno, c'è un modo per evitare che venga inserito nell'istruzione update? Ho provato a utilizzare "update_attributes" e a limitare l'hash ai campi di interesse, ma le rotaie aggiornano ancora tutti i campi serializzati.

Suggerimenti?

risposta

1

Sì, anche questo mi dava fastidio. Questo è quello che ho fatto per Rails 2.3.14 (o inferiore):

# 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 

Poi nel vostro uso del controller:

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 definire nel vostro modello se non si prevede alcuna modifica al campo serializzato una volta creato (ma update_attribute (: serialized_field => "aggiornare me" funziona ancora)

class Model < ActiveRecord::Base 
    serialize :serialized_field 

    def no_serialize_update 
    true 
    end 

end 
+0

sembra abbastanza promettente. Grazie! Ora che so cosa sovrascrivere, potrei farlo (in congiunzione con super) invece di usare il metodo di concatenazione, a meno che non ci siano avvertimenti nell'adottare tale approccio. Fammi sapere se vedi eventuali problemi se uso semplicemente override + super. – Tabrez

+0

Ho provato anche quello, ma ho notato che non funzionava. Il problema è che update_with_dirty è già caricato (attraverso la catena originale) prima che tu abbia la possibilità di sovrascriverlo e usare super. – Joris

+0

Capito. Grazie Joris! – Tabrez

2

Ecco una soluzione simile per Rails 3.1.3

!.

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

inserire il seguente codice in config/inizializzatori/

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

non funziona su binari 3.2.13 –

+0

Funziona (provato su RoR 3.2.18). Hai aggiunto: no_serialize_update def vero fine al modello che contiene il 'serialize' si desidera è solito essere aggiornati per default? –

1

mi sono imbattuto in questo problema oggi e ha finito per l'hacking mia serializzatore insieme ad un getter e setter. Per prima cosa ho rinominato il campo in #{column}_raw e poi ho utilizzato il codice seguente nel modello (per l'attributo media nel mio caso).

require 'json' 

... 

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

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

Ora gli aggiornamenti parziali funzionano perfettamente per me e il campo viene aggiornato solo quando i dati vengono effettivamente modificati.

+0

soluzione impressionante! sarei interessato a sapere se ci sono degli inconvenienti, forse delle prestazioni? dover analizzare il crudo ogni volta? –

1

Il problema con la risposta di Joris è che si aggancia alla catena alias_method_chain, disabilitando tutte le catene eseguite dopo (come update_with_callbacks che non vengono chiamati i problemi relativi ai trigger). Proverò a creare un diagramma per renderlo più facile da capire.

Si può iniziare con una catena come questo

update -> update_with_foo -> update_with_bar -> update_with_baz 

noti che update_without_foo punti a update_with_bar e update_without_bar a update_with_baz

Dal momento che non è possibile modificare direttamente update_with_bar per il funzionamento interno del alias_method_chain si potrebbe provare aggancio alla catena aggiungendo un nuovo collegamento (bar2) e chiamando update_without_bar, quindi:

alias_method_chain :update, :bar2 

Purtroppo, questo ti porterà la seguente catena:

update -> update_with_bar2 -> update_with_baz 

Così update_with_foo è andato!

Quindi, sapendo che alias_method_chain non ti consente di ridefinire _with metodi mia soluzione finora è stato quello di ridefinire update_without_dirty e fare la scelta dell'attributo lì.

1

Non proprio una soluzione, ma una buona soluzione in molti casi per me era semplicemente spostare le colonne serializzate su un modello associato - spesso questo in realtà era comunque un buon adattamento semantico.