Rails 3 sprawdzają, czy atrybut się zmienił

153

Musisz sprawdzić, czy blok atrybutów zmienił się przed aktualizacją w Railsach 3.

ulica1, ulica2, miasto, stan, kod pocztowy

Wiem, że przydałoby się coś takiego

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

Ale ten fragment kodu będzie NAPRAWDĘ długi. Czy istnieje czystszy sposób?

pcasa
źródło

Odpowiedzi:

282

Sprawdź ActiveModel :: Dirty (domyślnie dostępne we wszystkich modelach). Dokumentacja jest naprawdę dobra, ale pozwala na takie rzeczy, jak:

@user.street1_changed? # => true/false
Peter Brown
źródło
To nie pomaga, jeśli próbujesz sprawdzić, czy atrybut został zmieniony w formularzu (co wydaje się być tym, co robi OP).
Mark Fraser,
8
Jasne, że tak. Jeśli przypisujesz wartości za pomocą skrótu params, pochodzą one z formularza.
Peter Brown
a co jeśli chciałbym zrobić coś takiego: model.collection.changed_size_by? lub coś w rodzaju
Mauro Dias
@MauroDias musiałbyś napisać własną logikę, aby śledzić takie właściwości kolekcji.
Peter Brown
Spróbuję użyć do tego biblioteki testowej. może być obejściem, w każdym razie TY
Mauro Dias
54

W ten sposób rozwiązałem problem sprawdzania zmian w wielu atrybutach.

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

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

changedMetoda zwraca tablicę atrybutów zmienionych dla tego obiektu.

Obie @user.changedi attrssą tablicami, więc mogę uzyskać przecięcie (patrz ary & other arymetoda). Wynikiem przecięcia jest tablica. Wywołując any?tablicę, otrzymuję wartość true, jeśli istnieje co najmniej jedno przecięcie.

Również bardzo przydatna changed_attributesmetoda zwraca hash atrybutów z ich oryginalnymi wartościami, a changeszwraca hash atrybutów z ich oryginalnymi i nowymi wartościami (w tablicy).

Możesz sprawdzić APIDock, dla których wersje obsługują te metody.

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

campeterson
źródło
1
To miły sposób na zrobienie tego. Historycznie robiłem, if attrs.any?{|attr| @user.send("#{attr}_changed?")}gdy chcę sprawdzić, czy zmienił się jeden z kilku różnych atrybutów - oczywiście robię to tylko z atrybutami, które sam zdefiniowałem, ponieważ nie lubię wrzucać parametrów użytkownika do sendmetody. ;)
nzifnab
14

ActiveModel::Dirtynie zadziałało dla mnie, ponieważ @model.update_attributes()ukrył zmiany. A więc tak wykryłem zmiany w updatemetodzie w kontrolerze:

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

Jeśli próbujesz wykryć wiele zmian atrybutów, może to jednak spowodować bałagan. Prawdopodobnie nie powinienem tego robić w kontrolerze, ale ja.

hamstar
źródło
9
Za ile jest to warte, możesz to zrobić, z previous_changesktórym jest również domyślnie dostępny.
Jason Galuten
2
#update_attributesnie ukrywa zmian. Zapisuje rekord, więc model jest aktualizowany i nie ma żadnych zmian. Musisz zmienić pole w modelu i sprawdzić changed?przed jego zapisaniem. to znaczy. @ model.field = 'foo' lub@model.attributes = @model.attributes.merge(params[:model])
Grzegorz
9

Dla szyn 5.1+ callbacków

Od wersji Ruby on Rails 5.1 metody attribute_changed?i attribute_wasActiveRecord będą przestarzałe

Użyj saved_change_to_attribute?zamiastattribute_changed?

@user.saved_change_to_street1? # => true/false

Więcej przykładów tutaj

Lucas Andrade
źródło
2

Powyższe odpowiedzi są lepsze, ale dla wiedzy mamy też inne podejście, Zmieniono wartość kolumny 'catagory' dla obiektu (@design),

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

.Changes zwróci hash z kluczem jako nazwą kolumny i wartościami w postaci tablicy z dwiema wartościami [stara_wartość, nowa_wartość] dla każdej kolumny. Na przykład kategoria powyżej została zmieniona z „ABC” na „XYZ” @design,

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

Zmiana referencji w ROR

shubham mishra
źródło