2012-04-02 14 views
6

Questo è molto simile a Rails partial updates problem with hashes, ma la domanda non è stata ancora risposta IMHO.Arresta ActiveRecord salvando una colonna serializzata anche se non è stata modificata

Il problema è questo: ho un modello con una colonna serializzato:

class Import < AR::Base 
    serialize :data 

Nel mio caso, questi dati saranno, e dovrebbero, non cambia dopo il primo salvataggio/creazione del modello. Quindi voglio disabilitare la funzionalità di AR che salva sempre le colonne serializzate (che di solito è una buona idea, dato che non può rilevare quelle modifiche). Voglio disabilitare il salvataggio perché i dati possono essere abbastanza grandi e il modello verrà aggiornato frequentemente.

Ho già provato monkeypatching in ActiceRecord :: :: AttributeMethods sporco come questo:

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

ma questo sembra non avere alcun effetto. Qualcuno ha un'idea migliore?

Questo è sotto Rails 3.0.12

risposta

6

Quello che ho finito per fare, anche se non è proprio una risposta alla domanda iniziale, è la seguente:

class Import < AR::Base 
    belongs_to :storage 

class Storage < AR::Base 
    serialize :data 

... cioè. spostare la colonna di dati in un modello a sé stante e associarla al modello originale. Che in realtà è concettualmente un po 'più pulito.

+0

Questo in realtà ha molto senso, grazie per aver condiviso l'intuizione. Penso che ho intenzione di seguire lo stesso percorso e utilizzare un'associazione polimorfica tra il modello _Storage_ e altri che hanno colonne serializzate di sola lettura –

3

Ecco una soluzione cerotto scimmia brutto:

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 

Si dovrà ricordarsi di chiamare cache_spam !, o il vostro attributo serializzato non saranno mai salvati.