2012-11-05 13 views
6

Sto lavorando a un'applicazione Rails 3.2.8 che utilizza un'API REST homegrown per inserire nuovi dati. La logica di inserimento è generica per ogni endpoint e si traduce semplicemente in una chiamata a Model.save.Rails Sovrascrivi Salva per eseguire l'aggiornamento selettivo

Per uno dei tipi di modello, vorrei innanzitutto verificare se esiste un record preesistente e, in tal caso, aggiornare anziché inserire. Se il codice mi permettesse di interagire a livello di Controller, sarebbe facile usare find_or_create_by, tuttavia (credo) la mia unica opzione è di sovrascrivere il metodo di salvataggio nel Modello o usare un callback before_save.

Non riesco a trovare un modo per farlo funzionare, poiché qualsiasi chiamata a salvare o update_attributes all'interno del modello genera semplicemente un ciclo infinito (per ovvi motivi).

C'è un modo per utilizzare before_save o per sovrascrivere il salvataggio in modo tale da poter prima controllare se esiste un record con attributi xey e se sì recuperare quel record ed eseguire l'aggiornamento, altrimenti andare avanti con un ActiveRecord standard risparmia?

Ecco il mio codice nella sua forma attuale all'interno del modello di attività, che non funziona a causa della questione ciclo infinito:

def save 
    a = UserActivity.find_or_initialize_by_user_id_and_activity_id(user_id: user_id,  activity_id: activity_id) 
    a.update_attributes start_at: start_at, end_at: end_at..... 
end 
+0

mi sento come l'editing 'save' potrebbe aprire un'intera scatola di worms di problemi delicati con database. Hai considerato semplicemente di usare le istruzioni 'if - else' insieme a' update_attributes' e 'save'? –

risposta

7

Lei sembra essere bisogno find_or_create_by_* metodo.

Per evitare il ciclo, non si dovrebbe mettere questo in save metodo, ma in uno di questi due posti:

Opzione 1: livello di controller

nel controller in cui si instanciate questo UserActivity esempio, invece scrivere:

a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id) 
a.update_attributes start_at: start_at, end_at: end_at..... 

Opzione 2: metodo di classe

Se vi trovate a un ggiunta il codice qui sopra per diversi contrllers, un modo migliore sarebbe quello di definire un nuovo metodo di classe in UserActivity:

class UserActivity 
    def self.create_or_update_from_attrs(user_id, activity_id, start_at, end_at...) 
    a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id,  activity_id: activity_id) 
    a.update_attributes start_at: start_at, end_at: end_at..... 
    end 
end 

E nei controller, ovviamente:

UserActivity.create_or_update_from_attrs(...) 

Override Salva

Di Naturalmente, puoi anche ignorare il metodo save, ma questo duplica la funzionalità Rails (find_or_create_by...) e come tale viola DRY e potresti spararti il piede un po 'di tempo dopo, quando tale procedura sia conforme qualche altra situazione si esegue in, in modo da scoraggiare l'utilizzo di questo:

EDIT: aggiornati per evitare loop infinito

class UserActivity 
    def save 
    # If this is new record, check for existing and update that instead: 
    if new_record? && a = UserActivity.where(user_id: user_id, activity_id: activity_id).first 
     a.update_attributes start_at: start_at, end_at: end_at ... 
     return true # just to comply with Rails conventions   
    else 
     # just call super to save this record 
     super 
    end 
    end 
end 
+0

Grazie Lasa. Il problema è che il livello API è generico e chiama semplicemente salva su qualsiasi modello che interpreta come coinvolto (basato sulla chiamata URI). Quindi, non ho nessun aggancio al livello controller, che sono d'accordo è il posto giusto per mettere questa logica.Nel momento in cui sono coinvolto, il metodo di salvataggio è già stato chiamato sul modello. L'opzione per sovrascrivere il salvataggio produce ancora un ciclo infinito, poiché update_attributes comporta il salvataggio di nuovo richiamo sul modello. – JaySquat

+0

Ho aggiornato il mio codice - il secondo salvataggio dovrebbe chiamare 'super' perché il record non è' new_record? 'Più. – Laas

+0

Questo ha funzionato perfettamente, grazie! Ti voterei, ma non ho ancora abbastanza rappresentanti. – JaySquat