Cara menentukan apakah record baru saja dibuat atau diperbarui di after_save

96

#New_record? fungsi menentukan apakah rekaman telah disimpan. Tapi itu selalu salah after_save. Apakah ada cara untuk menentukan apakah record tersebut adalah record yang baru dibuat atau yang lama dari update?

Saya berharap untuk tidak menggunakan callback lain seperti before_createmenyetel bendera dalam model atau memerlukan kueri lain ke db.

Setiap saran dihargai.

Sunting: Perlu menentukannya dalam after_savekaitan, dan untuk kasus penggunaan khusus saya, tidak ada updated_atatau updated_oncap waktu

Aaron Qian
sumber
1
hmm mungkin berikan parameter di before_save? hanya berpikir keras
Perjalanan

Jawaban:

169

Saya ingin menggunakan ini untuk after_savepanggilan balik.

Solusi yang lebih sederhana adalah dengan menggunakan id_changed?(karena tidak akan berubah update) atau bahkan created_at_changed?jika kolom stempel waktu ada.

Pembaruan: Seperti yang ditunjukkan @mitsy, jika pemeriksaan ini diperlukan di luar panggilan balik, maka gunakan id_previously_changed?. Lihat dokumen .

Zubin
sumber
3
melalui ActiveModel :: Dirty
chaserx
6
Yang terbaik adalah membedakannya dengan after_update dan after_create. Callback bisa berbagi metode umum yang membutuhkan argumen untuk menunjukkan apakah itu pembuatan atau pembaruan.
matthuhiggins
2
Ini mungkin telah berubah. Setidaknya di Rails 4, callback after_save berjalan setelah callback after_create atau after_update (lihat panduan.rubyonrails.org/active_record_callbacks.html ).
Tandai
3
Memeriksa tidak satu pun dari bidang ini yang berfungsi di luar after_save.
fatuhoku
3
id_changed?akan menjadi false setelah record disimpan (setidaknya di luar hook). Dalam hal ini, Anda dapat menggunakanid_previously_changed?
mltsy
31

Tidak ada sihir rel di sini yang saya tahu, Anda harus melakukannya sendiri. Anda dapat membersihkannya menggunakan atribut virtual ...

Di kelas model Anda:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end
Jeff Paquette
sumber
26

Namun pilihan lain, bagi mereka yang melakukan memiliki updated_attimestamp:

if created_at == updated_at
  # it's a newly created record
end
colllin
sumber
Bukan ide yang buruk, tetapi tampaknya ini bisa menjadi bumerang dalam beberapa situasi (belum tentu anti peluru).
Ash Blue
Bergantung pada kapan migrasi dijalankan, catatan create_at dan update_at bisa dimatikan. Anda juga selalu menjalankan peluang seseorang memperbarui catatan tepat setelah menyimpannya pertama kali yang bisa membuat waktu tidak sinkron. Ini bukan ide yang buruk, hanya terasa seperti implementasi yang lebih anti peluru dapat ditambahkan.
Ash Blue
Juga mereka mungkin sama lama setelah rekaman pertama kali dibuat. Jika rekaman tidak diperbarui selama berbulan-bulan, rekaman itu akan terlihat seperti baru saja dibuat .
bschaeffer
1
@bschaeffer Maaf, pertanyaan saya adalah "apakah mungkin untuk created_atmenyamakan updated_atdalam after_savepanggilan balik kapan saja selain saat pertama kali dibuat?"
colllin
1
@colllin: Saat membuat record, created_atdan updated_atakan sama dalam after_savecallback. Dalam semua situasi lain, mereka tidak akan sama di after_savecallback.
bschaeffer
22

Ada after_createcallback yang hanya dipanggil jika recordnya adalah record baru, setelah disimpan . Ada juga after_updatepanggilan balik untuk digunakan jika ini adalah catatan yang sudah ada yang diubah dan disimpan. The after_savecallback disebut dalam kedua kasus, setelah baik after_createatau after_updatedisebut.

Menggunakan after_create jika Anda membutuhkan sesuatu untuk terjadi sekali setelah record baru disimpan.

Info lebih lanjut di sini: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Sharpone
sumber
1
Hei, terima kasih atas jawabannya, ini sangat membantu saya. Cheers man, +10 :)
Adam McArthur
1
Ini mungkin tidak benar sebenarnya. Jika Anda memiliki asosiasi dalam kreasi Anda, maka after_create dipanggil SEBELUM asosiasi dibuat, jadi jika Anda perlu memastikan SEMUANYA dibuat, maka Anda perlu menggunakan after_save
Niels Kristian
18

Karena objek telah disimpan, Anda perlu melihat perubahan sebelumnya. ID hanya boleh berubah setelah dibuat.

# true if this is a new record
@object.previous_changes[:id].any?

Ada juga variabel contoh @new_record_before_save. Anda dapat mengaksesnya dengan melakukan hal berikut:

# true if this is a new record
@object.instance_variable_get(:@new_record_before_save)

Keduanya sangat jelek, tetapi memungkinkan Anda mengetahui apakah objek tersebut baru dibuat. Semoga membantu!

Tom Rossi
sumber
Saat ini saya sedang dalam byebug dalam after_savepanggilan balik menggunakan Rails 4 dan tak satu pun dari ini bekerja untuk mengidentifikasi rekor baru ini.
MCB
Menurut saya itu @object.previous_changes[:id].any?cukup sederhana dan elegan. Ini berfungsi untuk saya setelah record diperbarui (saya tidak menyebutnya dari after_save).
thekingoftruth
1
@object.id_previously_changed?sedikit kurang jelek.
Mulia
@MCB di after_saveRails 4 yang ingin Anda lihat, changes[:id]bukan previous_changes[:id]. Namun ini berubah di Rails5.1 (lihat diskusi di github.com/rails/rails/pull/25337 )
gmcnaughton
1
previous_changes.key?(:id)untuk pemahaman yang lebih baik.
Sebastian Palma
3

Rails 5.1+ cara:

user = User.new
user.save!
user.saved_change_to_attribute?(:id) # => true
Jacka
sumber
1

Untuk Rails 4 (diperiksa pada 4.2.11.1) hasil changesdan previous_changesmetode adalah hash kosong {}pada pembuatan objek di dalamnya after_save. Jadi attribute_changed?metode seperti id_changed?tidak akan berfungsi seperti yang diharapkan.

Tetapi Anda dapat memanfaatkan pengetahuan ini dan - mengetahui bahwa setidaknya 1 atribut harus dalam changespembaruan - periksa apakah changeskosong. Setelah Anda mengonfirmasi bahwa itu kosong, Anda harus selama pembuatan objek:

after_save do
  if changes.empty?
    # code appropriate for object creation goes here ...
  end
end
cryptogopher
sumber
0

Saya suka lebih spesifik bahkan saya sadar itu :idseharusnya tidak berubah secara normal, tetapi

(byebug) id_change.first.nil?
true

Selalu lebih murah untuk menjadi spesifik daripada menemukan bug tak terduga yang sangat aneh.

Sama halnya jika saya mengharapkan trueflag dari argumen yang tidak dapat dipercaya

def foo?(flag)
  flag == true
end

Ini menghemat banyak waktu untuk tidak duduk di atas serangga aneh.

x'ES
sumber