Rails 3 controlla se l'attributo è cambiato


153

È necessario verificare se un blocco di attributi è stato modificato prima dell'aggiornamento in Rails 3.

street1, street2, città, stato, codice postale

So che potrei usare qualcosa del genere

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

Ma quel pezzo di codice sarà DAVVERO lungo. C'è un modo più pulito?

Risposte:


282

Dai un'occhiata a ActiveModel :: Dirty (disponibile su tutti i modelli per impostazione predefinita). La documentazione è davvero buona, ma ti consente di fare cose come:

@user.street1_changed? # => true/false

Questo non aiuta se stai cercando di vedere se un attributo è stato modificato in un modulo (che sembra essere ciò che l'OP sta facendo).
Mark Fraser,

8
Certo che lo fa. Se si assegnano i valori utilizzando l'hashs params, questi provengono dal modulo.
Peter Brown,

e se volessi fare qualcosa del genere: model.collection.changed_size_by? o qualcosa del genere
Mauro Dias,

@MauroDias dovresti scrivere la tua logica per tenere traccia delle proprietà della raccolta in questo modo.
Peter Brown,

Proverò ad usare un po 'di test lib per questo. può essere una soluzione alternativa, in ogni caso TY
Mauro Dias,

54

Ecco come ho risolto il problema di verificare la presenza di modifiche in più attributi.

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

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

Il changedmetodo restituisce una matrice degli attributi modificati per quell'oggetto.

Entrambi @user.changede attrssono array in modo da poter ottenere l'intersezione (vedi ary & other arymetodo). Il risultato dell'intersezione è un array. Chiamando any?l'array, divento vero se esiste almeno un incrocio.

Anche molto utile, il changed_attributesmetodo restituisce un hash degli attributi con i loro valori originali e changesrestituisce un hash degli attributi con i loro valori originali e nuovi (in un array).

Puoi controllare APIDock per quali versioni supportano questi metodi.

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


1
Questo è un bel modo di farlo. Storicamente l'ho fatto if attrs.any?{|attr| @user.send("#{attr}_changed?")}Quando voglio verificare se uno dei diversi attributi è cambiato - Certo, lo faccio solo con attrs che mi sono definito, perché non mi piace gettare parametri utente in un sendmetodo. ;)
nzifnab,

14

ActiveModel::Dirtynon ha funzionato per me perché @model.update_attributes()nascondeva i cambiamenti. Quindi è così che l'ho rilevato lo cambia in un updatemetodo in un controller:

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

Se stai provando a rilevare molte modifiche agli attributi, potrebbe diventare confuso. Probabilmente non dovrei farlo in un controller, ma meh.


9
Per quello che vale, puoi farlo con il previous_changesquale è disponibile anche per impostazione predefinita.
Jason Galuten,

2
#update_attributesnon nasconde le modifiche. Salva il record, quindi il modello viene aggiornato e non ci sono modifiche. È necessario modificare il campo nel modello e verificarlo changed?prima di salvarlo. vale a dire. @ model.field = 'foo' oppure@model.attributes = @model.attributes.merge(params[:model])
Grzegorz il

9

Per callback rails 5.1+

A partire da Ruby on Rails 5.1, i metodi attribute_changed?e attribute_wasActiveRecord saranno obsoleti

Usa saved_change_to_attribute?invece diattribute_changed?

@user.saved_change_to_street1? # => true/false

Altri esempi qui


2

Le risposte di cui sopra sono migliori ma, per quanto ne sappiamo, abbiamo anche un altro approccio: consente di cambiare il valore della colonna "catagoria" per un oggetto (@design),

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

.Changes restituirà un hash con chiave come nome della colonna e valori come una matrice con due valori [vecchio_valore, nuovo_valore] per ogni colonna. Ad esempio, la categoria di cui sopra viene modificata da "ABC" a "XYZ" di @design,

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

Per i riferimenti cambiare in ROR

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.