Wie erkenne ich Attributänderungen vom Modell?

84

Ich möchte eine Rückruffunktion in Schienen erstellen, die ausgeführt wird, nachdem ein Modell gespeichert wurde.

Ich habe dieses Modell, Anspruch, der ein Attribut 'Status' hat, das sich je nach Status des Anspruchs ändert. Mögliche Werte stehen noch aus, werden gebilligt, genehmigt, abgelehnt

Die Datenbank hat den Status "Status" mit dem Standardwert "Ausstehend".

Ich möchte bestimmte Aufgaben ausführen, nachdem das Modell beim ersten Mal erstellt oder von einem Status in einen anderen aktualisiert wurde, je nachdem, von welchem ​​Status es sich ändert.

Meine Idee ist es, eine Funktion im Modell zu haben:

    after_save :check_state

    def check_state
      # if status changed from nil to pending (created)
      do this

      # if status changed from pending to approved
      performthistask
     end

Meine Frage ist, wie ich vor der Änderung innerhalb des Modells nach dem vorherigen Wert suchen kann.

David C.
quelle

Antworten:

173

Sie sollten sich das ActiveModel :: Dirty- Modul ansehen : Sie sollten in der Lage sein, folgende Aktionen für Ihr Anspruchsmodell auszuführen:

claim.status_changed?  # returns true if 'status' attribute has changed
claim.status_was       # returns the previous value of 'status' attribute
claim.status_change    # => ['old value', 'new value'] returns the old and 
                       # new value for 'status' attribute

claim.name = 'Bob'
claim.changed # => ["name"]
claim.changes # => {"name" => ["Bill", "Bob"]}

Oh! die Freuden der Schienen!

Harish Shetty
quelle
6
Dies funktioniert nicht, nachdem das Modell gespeichert wurde, was er verlangt hat.
Tom Rossi
4
@ TomRossi, die dirtyAufrufe funktionieren in after_save(sowohl in Rails 2.3 als auch in 3.x). Ich habe es mehrmals benutzt.
Harish Shetty
11
@ TomRossi, die Dirty Flags werden nach dem Festschreiben zurückgesetzt, sodass sie in after_commitRückrufen, die in Rails 3.x eingeführt wurden , nicht verfügbar sind . Sie werden sicherlich in arbeiten after_save.
Harish Shetty
Ich hatte keine Ahnung! Ich dachte, sie würden zurückgesetzt, sobald es gespeichert wurde!
Tom Rossi
5
@ TomRossi Ich habe vor einigen Jahren mit der gleichen Annahme begonnen. Als ich versuchte, die schmutzigen Flags in after_save zu überprüfen, funktionierte es. Im Wesentlichen after_savehandelt es sich um einen Rückruf für einen Zustand zwischen after DMLund before_commit. Sie können die gesamte Transaktion beenden, after_saveindem Sie eine Ausnahme auslösen. Wenn Sie nach dem Speichern etwas tun möchten, ohne die aktuelle Operation zu beeinträchtigen, verwenden Sie after_commit:-)
Harish Shetty
38

Sie können dies verwenden

self.changed

Es gibt ein Array aller Spalten zurück, die sich in diesem Datensatz geändert haben

Sie können auch verwenden

self.changes

Dies gibt einen Hash von Spalten zurück, die sich geändert haben und vor und nach den Ergebnissen als Arrays

zeacuss
quelle
7
Nur eine kleine Anmerkung, um zu sagen, dass es nicht notwendig ist, self.diese zu verwenden - Sie können einfach sagen changedund changes.
user664833
@ user664833 Insbesondere können Sie selfim Modell selbst weglassen , aber Sie können diese für jedes Objekt mit object.changedund aufrufen object.changes. :)
Joshua Pinter
3

Ich empfehle Ihnen, sich eines der verfügbaren Plugins für Zustandsmaschinen anzusehen:

In beiden Fällen können Sie Zustände und Übergänge zwischen Zuständen einrichten. Sehr nützliche und einfache Möglichkeit, Ihre Anforderungen zu erfüllen.

Toby Hede
quelle
Ich versuche es mit Rubyist-Aasm. Nehmen wir an, ich habe die Klasse Claim <ActiveRecord :: Base include AASM von "ausstehend". Wenn ich Claim.create ausführen würde, ohne das Statusfeld auszufüllen (damit "ausstehend" ausgeführt wird), führt AASM dann die Methode "enter_pending" aus?
David C
1

Für Rails 5.1+ sollten Sie die aktive Datensatzattributmethode verwenden: saved_change_to_attribute?

saved_change_to_attribute? (attr_name, ** options) `

Hat sich dieses Attribut beim letzten Speichern geändert? Diese Methode kann als saved_change_to_name?statt aufgerufen werden saved_change_to_attribute?("name"). Benimmt sich ähnlich wie attribute_changed?. Diese Methode ist nützlich, um nach Rückrufen festzustellen, ob der zu speichernde Aufruf ein bestimmtes Attribut geändert hat.

Optionen

from Bei der Übergabe gibt diese Methode false zurück, es sei denn, der ursprüngliche Wert entspricht der angegebenen Option

to Bei der Übergabe gibt diese Methode false zurück, es sei denn, der Wert wurde auf den angegebenen Wert geändert

Ihr Modell sieht also so aus, wenn Sie eine Methode aufrufen möchten, die auf der Änderung des Attributwerts basiert:

class Claim < ApplicationRecord
  
  after_save :do_this, if: Proc.new { saved_change_to_status?(from: nil, to: 'pending') }

  after_save :do_that, if: Proc.new { saved_change_to_status?(from: 'pending', to: 'approved') }

  
  def do_this
    ..
    ..
  end

  def do_that
    ..
    ..
  end

end

Und wenn Sie im Rückruf nicht nach Wertänderungen suchen möchten, können Sie Folgendes tun:

class Claim < ApplicationRecord

  after_save: :do_this, if: saved_change_to_status?


  def do_this
    ..
    ..
  end

end
Rajkaran Mishra
quelle
0

Ich habe die Frage an vielen Stellen auftauchen sehen, deshalb habe ich ein kleines Rubingem dafür geschrieben, um den Code ein wenig schöner zu machen (und eine Million if / else-Anweisungen überall zu vermeiden): https://github.com/ronna-s / on_change . Ich hoffe das hilft.

Ronna
quelle
-1

Mit einer gut getesteten Lösung wie dem Juwel state_machine sind Sie viel besser dran .

Paz Aricha
quelle