Rails 3 memeriksa apakah atribut berubah

153

Perlu memeriksa apakah blok atribut telah berubah sebelum memperbarui di Rails 3.

street1, street2, kota, negara bagian, kode pos

Saya tahu saya bisa menggunakan sesuatu seperti

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

Tapi sepotong kode itu akan BENAR-BENAR panjang. Apakah ada cara yang lebih bersih?

pcasa
sumber

Jawaban:

282

Lihat ActiveModel :: Dirty (tersedia pada semua model secara default). Dokumentasinya sangat bagus, tetapi memungkinkan Anda melakukan hal-hal seperti:

@user.street1_changed? # => true/false
Peter Brown
sumber
Ini tidak membantu jika Anda mencoba untuk melihat apakah atribut diubah dalam bentuk (yang tampaknya adalah apa yang dilakukan OP).
Mark Fraser
8
Tentu saja. Jika Anda menetapkan nilai menggunakan hash params, ini berasal dari formulir.
Peter Brown
bagaimana jika saya ingin melakukan sesuatu seperti ini: model.collection.changed_size_by? atau sesuatu seperti
Mauro Dias
@ MauroDias Anda harus menulis logika Anda sendiri untuk melacak properti koleksi seperti itu.
Peter Brown
Saya akan mencoba menggunakan beberapa lib tes untuk ini. mungkin merupakan solusi, toh TY
Mauro Dias
54

Ini adalah bagaimana saya memecahkan masalah memeriksa perubahan di banyak atribut.

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

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

The changedMetode mengembalikan array atribut berubah untuk objek.

Keduanya @user.changeddan attrsarray sehingga saya bisa mendapatkan persimpangan (lihat ary & other arymetode). Hasil persimpangan adalah array. Dengan memanggil any?array, saya menjadi kenyataan jika setidaknya ada satu persimpangan.

Juga sangat berguna, changed_attributesmetode mengembalikan hash atribut dengan nilai aslinya dan changesmengembalikan hash atribut dengan nilai asli dan baru (dalam array).

Anda dapat memeriksa APIDock untuk versi mana yang mendukung metode ini.

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

campeterson
sumber
1
Ini cara yang bagus untuk melakukannya. Secara historis saya sudah selesai if attrs.any?{|attr| @user.send("#{attr}_changed?")}Ketika saya ingin memeriksa apakah salah satu dari beberapa atribut berbeda telah berubah - Tentu saja, saya hanya melakukan ini dengan attr yang telah saya tentukan sendiri, karena saya tidak suka melempar params pengguna ke dalam suatu sendmetode. ;)
nzifnab
14

ActiveModel::Dirtytidak bekerja untuk saya karena @model.update_attributes()menyembunyikan perubahan. Jadi ini adalah bagaimana saya mendeteksi perubahan dalam suatu updatemetode di 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

Jika Anda mencoba mendeteksi banyak perubahan atribut, itu bisa berantakan. Mungkin tidak seharusnya melakukan ini di controller, tapi meh.

hamstar
sumber
9
Untuk apa nilainya, Anda dapat melakukan ini dengan previous_changesyang juga tersedia secara default.
Jason Galuten
2
#update_attributestidak menyembunyikan perubahan. Ini menyimpan catatan, sehingga model diperbarui dan tidak ada perubahan. Anda perlu mengubah bidang dalam model dan memeriksa changed?sebelum Anda menyimpannya. yaitu. @ model.field = 'foo' atau@model.attributes = @model.attributes.merge(params[:model])
Grzegorz
9

Untuk rel 5.1+ callback

Pada Ruby on Rails 5.1, metode attribute_changed?dan attribute_wasActiveRecord akan ditinggalkan

Gunakan saved_change_to_attribute?sebagai gantiattribute_changed?

@user.saved_change_to_street1? # => true/false

Lebih banyak contoh di sini

Lucas Andrade
sumber
2

Di atas jawaban lebih baik tetapi untuk pengetahuan kami memiliki pendekatan lain juga, Mari kita nilai kolom 'berubah' untuk objek (@desain),

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

The .changes akan mengembalikan hash dengan kunci sebagai nama kolom dan nilai-nilai sebagai array dengan dua nilai [old_value, new_value] untuk setiap kolom. Misalnya kategori untuk di atas diubah dari 'ABC' ke 'XYZ' dari @design,

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

Untuk referensi, ubah ROR

shubham mishra
sumber