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.