29 Stimmen

Verwendung des after_save-Callbacks zur Änderung desselben Objekts, ohne den Callback erneut auszulösen (Rekursion)

Wenn ich einem ActiveRecord-Modell einen after_save-Callback hinzufüge und bei diesem Callback update_attribute verwende, um das Objekt zu ändern, wird der Callback erneut aufgerufen, und es kommt zu einem "Stack Overflow" (hehe, konnte nicht widerstehen).

Ist es möglich, dieses Verhalten zu vermeiden, vielleicht Deaktivieren des Rückrufs während seiner Ausführung? Oder gibt es einen anderen Ansatz?

Gracias.

4voto

Patrick McKenzie Punkte 3996

Dieser Code versucht nicht einmal, Threading oder Gleichzeitigkeitsprobleme anzugehen, ähnlich wie Rails selbst. Wenn Sie diese Funktion benötigen, beachten Sie dies!

Grundsätzlich besteht die Idee darin, zu zählen, auf welcher Ebene der rekursiven Aufrufe von "save" man sich befindet, und after_save nur zuzulassen, wenn man die oberste Ebene verlässt. Sie werden auch eine Ausnahmebehandlung einbauen wollen.

def before_save
  @attempted_save_level ||= 0
  @attempted_save_level += 1
end

def after_save
  if (@attempted_save_level == 1) 
     #fill in logic here

     save  #fires before_save, incrementing save_level to 2, then after_save, which returns without taking action

     #fill in logic here 

  end
  @attempted_save_level -= 1  # reset the "prevent infinite recursion" flag 
end

3voto

Codebeef Punkte 42570

Wenn Sie before_save verwenden, können Sie alle zusätzlichen Parameter ändern, bevor die Speicherung abgeschlossen ist, so dass Sie save nicht explizit aufrufen müssen.

2voto

Ivan Punkte 86964

Danke Leute, das Problem ist, dass ich die andere auch Objekte (Geschwister, wenn man so will)... vergaß diesen Teil zu erwähnen...

before_save kommt also nicht in Frage, denn wenn das Speichern fehlschlägt, müssten alle Änderungen an den anderen Objekten rückgängig gemacht werden, und das könnte unschön werden :)

2voto

Mike Punkte 8693

Der Trick ist, dass man einfach #update_column :

  • Validierungen werden übersprungen.
  • Rückrufe werden übersprungen.
  • updated_at/updated_on werden nicht aktualisiert.

Außerdem wird lediglich eine einzige schnelle Aktualisierungsabfrage an die Datenbank gestellt.

http://apidock.com/rails/ActiveRecord/Persistence/update_columns

1voto

Ich hatte dieses Problem auch. Ich muss ein Attribut speichern, das von der Objekt-ID abhängt. Ich habe es gelöst, indem ich einen bedingten Aufruf für den Rückruf verwendet habe ...

Class Foo << ActiveRecord::Base  
    after_save :init_bar_attr, :if => "bar_attr.nil?"    # just make sure this is false after the callback runs

    def init_bar_attr    
        self.bar_attr = "my id is: #{self.id}"    

        # careful now, let's save only if we're sure the triggering condition will fail    
        self.save if bar_attr
    end

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X