2013-03-27 17 views
9

Ich habe ein Beispiel Aktion in einem Controller.RAILS 3 - Transaktionen in Steuerungen

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

Wie füge ich Transaktionen für diesen Code hinzu? Ich versuche, mit diesem Beispielcode:

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

Aber es erzeugt diesen Fehler:

undefined method `transaction' 

Ich las über Transaktionen in Controllern ist eine schlechte Praxis, aber ich weiß nicht, warum ist der Grund (http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)

In dem Beispiel, wenn Produkt erstellt und gespeichert wurde und das Client-Update fehlschlägt ... Schienen dürfen nichts tun.

danke.

Antwort

25

Sie können eine Transaktion in einem Controller verwenden, wenn Sie dies wirklich möchten. Wie Sie bemerkt haben, ist es eine schlechte Übung, aber wenn Sie es tun wollen, rufen Sie einfach Product.transaction do statt transaction do. transaction ist eine Klassenmethode unter ActiveRecord::Base, daher müssen Sie sie in einer ActiveRecord-abgeleiteten Klasse aufrufen. Jede Modellklasse in Ihrer Anwendung reicht aus (Vorsicht, wenn Sie sich mit verschiedenen Datenbanken für verschiedene Modelle verbinden, das ist vielleicht nicht wahr ... aber das tun Sie wahrscheinlich nicht).

Der Grund, warum dies eine schlechte Praxis ist, ist, dass es Probleme nicht ordnungsgemäß nach dem MVC-Paradigma trennt. Ihr Controller sollte sich nicht so sehr mit der Implementierung Ihrer Datenpersistenz befassen. Ein besserer Ansatz wäre es, eine Methode zu hinzuzufügen. Vielleicht so etwas wie folgt aus:

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

Dann statt product.save in Ihrem Controller zu rufen, rufen product.save_and_update_client_create_time. Möglicherweise müssen Sie auch client an diese Methode übergeben. Es ist unklar, aus welchem ​​Code client stammt. Wenn es ein Attribut auf product ist, sollte die obige Methode funktionieren.

Es gibt bessere, mehr Railsy Möglichkeiten, dies auch zu tun, vor allem, wenn ein product über seine client weiß, ohne irgendwelche Controller-Daten. Dann können Sie einfach einen after_save Rückruf verwenden, wie folgt aus (in den Product Klasse):

after_save :update_client 

private 

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

dann jedes Mal, wenn ein Product gespeichert wird, wird das Feld auf dem zugehörigen Client aktualisiert werden. Sie müssen möglicherweise einen Code einführen, um zuerst nach der Existenz eines client zu suchen.

Der Vorteil der Verwendung von Rückrufen besteht neben saubererem Code darin, dass die gesamte Rückrufkette in einer einzigen Transaktion zusammen mit dem Speichervorgang ausgeführt wird. Sie müssen die Transaktion nicht manuell erstellen. Sie können mehr über Rückrufe in der Rails documentation lesen.

+0

Vielen Dank Jim, du hilfst mir wirklich bei deiner Erklärung! – user1364684

+3

Wenn transaktionale Logik in ein Modell eingefügt wird, wären Transaktionen dann nicht auf ein Modell beschränkt, um die separate Problemregel nicht zu trennen? In der Regel besteht eine hohe Wahrscheinlichkeit, dass sich Transaktionen über mehrere Modelle erstrecken, die auf DB-Ebene nicht notwendigerweise miteinander in Beziehung stehen. – xSNRG

+1

Ja, ich hatte eine Veränderung des Herzens in diesem speziellen Aspekt meines Kommentars. Ich mag die Idee, es aus dem Controller herauszuhalten, aber Multi-Modell-Interaktionen sollten irgendwo verpackt werden.Vielleicht eine andere Klasse, aber in manchen Situationen ist der Controller vielleicht der richtige Ort. –