2010-10-07 10 views
5

Fondamentalmente quello che voglio fare è registrare un'azione su MyModel nella tabella di MyModelLog. Ecco alcuni pseudo codice:Come salvare qualcosa nel database dopo le convalide di ActiveRecord fallite?

class MyModel < ActiveRecord::Base 
    validate :something 

    def something 
    # test 
    errors.add(:data, "bug!!") 
    end 
end 

ho anche un modello simile a questo:

class MyModelLog < ActiveRecord::Base 

    def self.log_something 
    self.create(:log => "something happened") 
    end 

end 

Per accedere Ho cercato di:

  • Aggiungere MyModelLog.log_something nel metodo di somethingMyModel

  • Chiama MyModelLog.log_something sulla after_validation callback di MyModel

In entrambi i casi la creazione è rotolato indietro quando la convalida non riesce, perché è nella transazione convalida. Naturalmente voglio anche loggare quando falliscono le validazioni. Non voglio veramente accedere a un file o da qualche altra parte rispetto al database perché ho bisogno delle relazioni delle voci di registro con altri modelli e della capacità di fare richieste.

Quali sono le mie opzioni?

+0

Simile tipo di domanda: http://stackoverflow.com/questions/3685912/how-do-i-exclude-a-model-from-a-transaction-in-activerecord/3686035#3686035 – Shadwell

risposta

0

È possibile utilizzare una transazione nidificata. In questo modo il codice nel callback viene eseguito in una transazione diversa dalla convalida non riuscita. Le documentazioni Rails per ActiveRecord::Transactions::ClassMethods discutono di come è fatto.

+2

Potrei non capire il tuo punto, ma se faccio una transazione annidata per log_qualcosa, poi quando la transazione "genitore" viene sottoposta a rollback, il bambino viene sottoposto a rollback anche – marcgg

1

Non sono sicuro se si applica a te, ma presumo che stai cercando di salvare/creare un modello dal controller. Nel controller è facile controllare l'esito di tale azione, e molto probabilmente lo fai già per fornire all'utente un flash utile; così puoi facilmente registrare un messaggio appropriato lì.

Suppongo anche che non si utilizzino transazioni esplicite, quindi se lo gestisci nel controller, è fuori dalla transazione (ogni operazione di salvataggio e distruzione nella propria transazione).

Cosa ne pensi?

+0

grazie per la risposta, ma potrei finire con molti di quei log in posti diversi quindi preferirei tenerli nel mio modello – marcgg

+0

Il vantaggio di farlo nel controller è che è esplicito e appartiene alla gestione di un errore. Per evitare la duplicazione del codice, è possibile mixare il comportamento, creare un metodo che registra l'errore e impostare il flash, ad esempio in un unico comando? D'altra parte, mi piace anche l'idea degli osservatori, non sono sicuro che operino in una transazione diversa. – nathanvda

3

Questo dovrebbe essere adatto per un Observer? Non sono sicuro, ma spero che esista al di fuori della transazione ... Ho un'esigenza simile in cui potrei voler cancellare un record sull'aggiornamento ...

+0

Buona idea, ci penserò e vedrò se si adatterebbe a quello che sto cercando di fare – marcgg

+1

Ulteriori ricerche mostrano che questo potrebbe non funzionare - Anche gli osservatori hanno luogo all'interno della transazione. Guarda after_commit in rails 3 per eseguire qualcosa dopo il commit ... c'è anche un after_rollback – DGM

8

Le transazioni nidificate sembrano funzionare in MySQL .

Ecco quello che ho provato su un rotaie appena generati (con MySQL) progetto:

./script/generate model Event title:string --skip-timestamps --skip-fixture 

./script/generate model EventLog error_message:text --skip-fixture 

class Event < ActiveRecord::Base                                  
    validates_presence_of :title                                   
    after_validation_on_create :log_errors                                

    def log_errors                                      
    EventLog.log_error(self) if errors.on(:title).present?                            
    end                                         
end 

class EventLog < ActiveRecord::Base                                  
    def self.log_error(event)                                    
    connection.execute('BEGIN') # If I do transaction do then it doesn't work. 
    create :error_message => event.errors.on(:title)                        
    connection.execute('COMMIT')                                  
    end                                         
end 

# And then in script/console: 
>> Event.new.save 
=> false 
>> EventLog.all 
=> [#<EventLog id: 1, error_message: "can't be blank", created_at: "2010-10-22 13:17:41", updated_at: "2010-10-22 13:17:41">] 
>> Event.all 
=> [] 

Forse Ho più semplificato, o manca qualche punto.

+0

grazie per la risposta. tuttavia questo sembra molto dipendente da mysql e non sono sicuro che sarebbe una soluzione valida. Inoltre, desidero registrare gli errori nei metodi di convalida poiché non registrerei solo il contenuto degli errori, ma anche i risultati di alcune chiamate API. Comunque, buon punto – marcgg

1

MyModelLog.log_something deve essere eseguito utilizzando una connessione diversa.

È possibile fare in modo che il modello MyModelLog utilizzi sempre una connessione diversa utilizzando establish_connection.

class MyModelLog < ActiveRecord::Base 
    establish_connection Rails.env # Use different connection 

    def self.log_something 
    self.create(:log => "something happened") 
    end 
end 

Non so se questo è il modo giusto per fare il logging !!

2

Ho risolto un problema in questo modo sfruttando l'ambito della variabile di Ruby. Fondamentalmente ho dichiarato una variabile error al di fuori di un blocco di transazione, quindi catturare, memorizzare il messaggio di registro e sollevare nuovamente l'errore.

Sembra qualcosa di simile a questo:

def something 
    error = nil 
    ActiveRecord::Base.transaction do 
     begin 
      # place codez here 
     rescue ActiveRecord::Rollback => e 
      error = e.message 
      raise ActiveRecord::Rollback 
     end 
    end 
    MyModelLog.log_something(error) unless error.nil? 
end 

Dichiarando la error esterno variabile della portata dell'operazione il contenuto della variabile persistono anche dopo che la transazione è uscito.

Problemi correlati