2013-03-27 14 views
9

Ho un esempio di azione in un controller.RAILS 3 - Transazioni nei controller

def some_action 
product = Product.new 
product.name = "namepro" 
    if product.save 
    client.update_attribute(:product_id,product.id) 
    end 
end 

Come aggiungere transazioni per questo codice? Provo con questo codice di esempio:

def some_action 
**transaction do** 
    product = Product.new 
    product.name = "namepro" 
    if product.save 
    client.update_attribute(:product_create,Time.now) 
    end 
**end** 
end 

ma produce questo errore:

undefined method `transaction' 

ho letto su come utilizzare le transazioni in Controller è una cattiva pratica, ma io non so perché è la ragione (http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)

Nell'esempio, se il prodotto è stato creato e salvato e l'aggiornamento del client non riesce ... Rails non deve fare nulla.

grazie.

risposta

25

È possibile utilizzare una transazione in un controller se si desidera veramente. Come hai notato, è una cattiva pratica, ma se vuoi farlo, chiama il numero Product.transaction do anziché transaction do. transaction è un metodo di classe su ActiveRecord::Base, quindi è necessario chiamarlo su una classe derivata da ActiveRecord. Qualsiasi classe di modello nella tua applicazione farà (caveat picking: se ti connetti a database diversi per modelli diversi, potrebbe non essere vero ... ma probabilmente non lo farai).

Il motivo per cui questa è una cattiva pratica è che non separa correttamente le preoccupazioni secondo il paradigma MVC. Il tuo controllore non dovrebbe essere così preoccupato dell'implementazione della persistenza dei dati. Un approccio migliore sarebbe aggiungere un metodo a Product. Forse qualcosa di simile:

def save_and_update_create_time 
    transaction do 
    if save 
     client.update_attribute(:product_create, Time.now) 
    end 
    end 
end 

Poi invece di chiamare product.save nel controller, chiamano product.save_and_update_client_create_time. Potrebbe essere necessario passare anche a ; non è chiaro dal tuo codice da cui proviene . Se si tratta di un attributo su product, il metodo sopra dovrebbe funzionare.

Ci sono modi migliori, più Railsy per fare anche questo, soprattutto se uno product conosce il suo client senza bisogno di dati del controller. Poi si può semplicemente utilizzare un callback after_save, come questo (aggiungi al Product classe):

after_save :update_client 

private 

def update_client(product) 
    product.client.update_attribute(:product_create, Time.now) 
end 

Poi ogni volta che un Product viene salvato, il campo sul client associato sarà aggiornato. Probabilmente dovrai prima introdurre un codice per verificare l'esistenza di uno client.

Il vantaggio dell'utilizzo di callback, oltre al codice più pulito, è che l'intera catena di callback viene eseguita in un'unica transazione insieme al salvataggio; non è necessario creare manualmente la transazione. Puoi leggere ulteriori informazioni sui callback nello Rails documentation.

+0

Grazie mille Jim, mi aiuti davvero con la tua spiegazione! – user1364684

+3

Se la logica transazionale è inserita in un modello, le transazioni non saranno limitate a un modello quindi, per non rompere la regola delle preoccupazioni separate? Di solito c'è un'alta probabilità che le transazioni si estendano su più Modelli, che non sono necessariamente correlati l'uno all'altro a livello di DB. – xSNRG

+1

Sì, ho avuto un cambiamento di cuore su quel particolare aspetto del mio commento. Mi piace l'idea di tenerlo fuori dal controller, ma le interazioni multi-modello dovrebbero essere avvolte da qualche parte.Forse un'altra classe, ma in alcune situazioni il controller potrebbe essere il posto giusto dopo tutto. –