Schienen 3 prüfen, ob sich das Attribut geändert hat

153

Vor dem Update in Rails 3 muss überprüft werden, ob sich ein Attributblock geändert hat.

Straße1, Straße2, Stadt, Bundesland, Postleitzahl

Ich weiß, ich könnte so etwas gebrauchen

if @user.street1 != params[:user][:street1]
  then do something....
end

Aber dieser Code wird WIRKLICH lang sein. Gibt es einen saubereren Weg?

pcasa
quelle

Antworten:

282

Schauen Sie sich ActiveModel :: Dirty an (standardmäßig für alle Modelle verfügbar). Die Dokumentation ist wirklich gut, aber Sie können damit Folgendes tun:

@user.street1_changed? # => true/false
Peter Brown
quelle
Dies hilft nicht, wenn Sie versuchen zu sehen, ob ein Attribut in einem Formular geändert wurde (was anscheinend das ist, was das OP tut).
Mark Fraser
8
Sicher tut es das. Wenn Sie die Werte mit dem Parameter-Hash zuweisen, stammen diese aus dem Formular.
Peter Brown
Was wäre, wenn ich so etwas machen möchte: model.collection.changed_size_by? oder so ähnlich
Mauro Dias
@MauroDias Sie müssten Ihre eigene Logik schreiben, um solche Sammlungseigenschaften zu verfolgen.
Peter Brown
Ich werde versuchen, dafür eine Testbibliothek zu verwenden. kann sowieso eine Problemumgehung sein TY
Mauro Dias
54

Auf diese Weise habe ich das Problem gelöst, nach Änderungen in mehreren Attributen zu suchen.

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

Die changedMethode gibt ein Array der für dieses Objekt geänderten Attribute zurück.

Beide @user.changedund attrssind Arrays, damit ich den Schnittpunkt erhalten kann (siehe ary & other aryMethode). Das Ergebnis der Schnittmenge ist ein Array. Wenn any?ich das Array aufrufe, werde ich wahr, wenn es mindestens eine Kreuzung gibt.

Ebenfalls sehr nützlich ist, dass die changed_attributesMethode einen Hash der Attribute mit ihren ursprünglichen Werten und changeseinen Hash der Attribute mit ihren ursprünglichen und neuen Werten (in einem Array) zurückgibt.

Sie können APIDock überprüfen, für welche Versionen diese Methoden unterstützt werden.

http://apidock.com/rails/ActiveModel/Dirty

Campeterson
quelle
1
Dies ist eine schöne Art, es zu tun. Historisch habe ich getan if attrs.any?{|attr| @user.send("#{attr}_changed?")}Wenn ich überprüfen möchte, ob sich eines von mehreren verschiedenen Attributen geändert hat - Natürlich mache ich dies nur mit attrs, die ich selbst definiert habe, weil ich es nicht mag, Benutzerparameter in eine sendMethode zu werfen . ;)
Nzifnab
14

ActiveModel::Dirtyhat bei mir nicht funktioniert, weil @model.update_attributes()die Änderungen versteckt waren. So habe ich Änderungen in einer updateMethode in einem Controller festgestellt :

def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

Wenn Sie versuchen, viele Attributänderungen zu erkennen, kann dies jedoch zu Problemen führen. Wahrscheinlich sollte das nicht in einem Controller gemacht werden, aber meh.

Hamstar
quelle
9
Für das, was es wert ist, können Sie dies tun, previous_changeswas auch standardmäßig verfügbar ist.
Jason Galuten
2
#update_attributesversteckt die Änderungen nicht. Der Datensatz wird gespeichert, sodass das Modell aktualisiert wird und keine Änderungen vorgenommen werden. Sie müssen das Feld im Modell ändern und prüfen, changed?bevor Sie es speichern. dh. @ model.field = 'foo' oder@model.attributes = @model.attributes.merge(params[:model])
Grzegorz
9

Für Rails 5.1+ Rückrufe

Ab Ruby on Rails 5.1 sind die Methoden attribute_changed?und attribute_wasActiveRecord veraltet

Verwenden Sie saved_change_to_attribute?anstelle vonattribute_changed?

@user.saved_change_to_street1? # => true/false

Weitere Beispiele hier

Lucas Andrade
quelle
2

Die obigen Antworten sind besser, aber aus Wissensgründen haben wir auch einen anderen Ansatz: Lässt den Spaltenwert 'Kategorie' für ein Objekt (@design) ändern.

@design.changes.has_key?('catagory')

Die .changes geben einen Hash mit dem Schlüssel als Spaltennamen und den Werten als Array mit zwei Werten [alter_Wert, neuer_Wert] für jede Spalte zurück. Zum Beispiel wird die Kategorie für oben von 'ABC' in 'XYZ' von @design geändert.

@design.changes   # => {} 
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }

Für Referenzen ändern Sie in ROR

Shubham Mishra
quelle