วิธีตรวจสอบว่าเพิ่งสร้างหรืออัปเดตเรกคอร์ดใน after_save


98

#new_record? ฟังก์ชันกำหนดว่าบันทึกถูกบันทึกหรือไม่ แต่มันมักจะเป็นเท็จในafter_saveเบ็ด มีวิธีตรวจสอบว่าเรกคอร์ดที่สร้างขึ้นใหม่หรือบันทึกเก่าจากการอัปเดตหรือไม่?

ฉันหวังว่าจะไม่ใช้การโทรกลับอื่นเช่นbefore_createเพื่อตั้งค่าสถานะในแบบจำลองหรือต้องการแบบสอบถามอื่นในฐานข้อมูล

คำแนะนำใด ๆ ที่ชื่นชม

แก้ไข: จำเป็นต้องตรวจสอบในafter_savehook และสำหรับกรณีการใช้งานเฉพาะของฉันไม่มีupdated_atหรือupdated_onประทับเวลา


1
อืมอาจจะส่งพารามิเตอร์ใน before_save? แค่คิดดัง ๆ
ทริป

คำตอบ:


170

ฉันต้องการใช้สิ่งนี้เพื่อafter_saveติดต่อกลับ

วิธีแก้ปัญหาที่ง่ายกว่าคือใช้id_changed?(เนื่องจากจะไม่เปลี่ยนupdate) หรือแม้ว่าcreated_at_changed?คอลัมน์การประทับเวลาจะปรากฏอยู่

ปรับปรุง: ในฐานะที่เป็น @mitsy id_previously_changed?จุดออกถ้าการตรวจสอบนี้เป็นสิ่งจำเป็นที่ด้านนอกของการเรียกกลับแล้วใช้ ดูเอกสาร



6
เป็นการดีที่สุดที่จะแยกความแตกต่างด้วย after_update และ after_create การเรียกกลับสามารถแชร์วิธีการทั่วไปที่ใช้อาร์กิวเมนต์เพื่อระบุว่าเป็นการสร้างหรืออัปเดต
matthuhiggins

2
สิ่งนี้อาจมีการเปลี่ยนแปลง อย่างน้อยใน Rails 4 การเรียกกลับ after_save จะรันหลังจากการเรียกกลับ after_create หรือ after_update (ดูที่guide.rubyonrails.org/active_record_callbacks.html )
มาร์ค

3
after_saveตรวจสอบค่าของเขตข้อมูลเหล่านี้ทำงานนอก
fatuhoku

3
id_changed?จะเป็นเท็จหลังจากบันทึกบันทึกแล้ว (นอก hooks เป็นอย่างน้อย) ในกรณีนี้คุณสามารถใช้id_previously_changed?
mltsy

31

ไม่มีรางวิเศษที่ฉันรู้จักคุณจะต้องทำเอง คุณสามารถล้างสิ่งนี้โดยใช้แอตทริบิวต์เสมือน ...

ในคลาสโมเดลของคุณ:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end

27

แต่อีกทางเลือกหนึ่งสำหรับผู้ที่ทำมีupdated_atการประทับเวลา:

if created_at == updated_at
  # it's a newly created record
end

ไม่ใช่ความคิดที่ไม่ดี แต่ดูเหมือนว่าสิ่งนี้อาจย้อนกลับไปในบางสถานการณ์ (ไม่จำเป็นต้องมีสัญลักษณ์แสดงหัวข้อย่อย)
Ash Blue

ขึ้นอยู่กับเวลาที่เรียกใช้การย้ายข้อมูลระเบียน created_at และ updated_at อาจปิดอยู่ นอกจากนี้คุณมักจะมีโอกาสที่จะมีใครบางคนอัปเดตบันทึกทันทีหลังจากบันทึกในครั้งแรกซึ่งอาจทำให้เวลาไม่ตรงกัน ไม่ใช่ความคิดที่ไม่ดีเพียงแค่รู้สึกว่าสามารถเพิ่มการใช้งานสัญลักษณ์แสดงหัวข้อย่อยเพิ่มเติมได้
Ash Blue

นอกจากนี้อาจยาวเท่ากันหลังจากสร้างเรกคอร์ดครั้งแรก หากบันทึกจะไม่ปรับปรุงสำหรับเดือนก็จะมีลักษณะเหมือนมันถูกสร้างขึ้นเพียง
bschaeffer

1
@bschaeffer ขออภัยคำถามของฉันคือ "เป็นไปได้ไหมที่created_atจะเรียกคืนเท่ากันupdated_atในafter_saveเวลาอื่นนอกเหนือจากที่สร้างครั้งแรก"
colllin

1
@colllin: เมื่อสร้างบันทึกcreated_atและupdated_atจะเท่ากันในการafter_saveโทรกลับ ในสถานการณ์อื่น ๆ การafter_saveเรียกกลับจะไม่เท่ากัน
bschaeffer

22

มีการafter_createเรียกกลับซึ่งจะเรียกเฉพาะในกรณีที่บันทึกเป็นระเบียนใหม่หลังจากบันทึกแล้ว นอกจากนี้ยังมีการafter_updateโทรกลับเพื่อใช้หากนี่เป็นบันทึกที่มีอยู่ซึ่งถูกเปลี่ยนแปลงและบันทึกไว้ การafter_saveโทรกลับจะถูกเรียกในทั้งสองกรณีหลังจากafter_createหรือafter_updateถูกเรียก

ใช้after_createหากคุณต้องการให้บางสิ่งเกิดขึ้นหนึ่งครั้งหลังจากบันทึกใหม่แล้ว

ข้อมูลเพิ่มเติมที่นี่: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html


1
สวัสดีขอบคุณสำหรับคำตอบสิ่งนี้ช่วยฉันได้มาก Cheers man, +10 :)
Adam McArthur

1
สิ่งนี้อาจไม่เป็นความจริง หากคุณมีการเชื่อมโยงในการสร้างของคุณ after_create จะถูกเรียกว่าก่อนที่จะสร้างการเชื่อมโยงดังนั้นหากคุณต้องการให้แน่ใจว่าทุกอย่างถูกสร้างขึ้นคุณจะต้องใช้ after_save
Niels Kristian

18

เนื่องจากวัตถุได้รับการบันทึกแล้วคุณจะต้องดูการเปลี่ยนแปลงก่อนหน้านี้ รหัสควรเปลี่ยนหลังจากสร้างแล้วเท่านั้น

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

@new_record_before_saveนอกจากนี้ยังมีตัวแปรเช่น คุณสามารถเข้าถึงได้โดยทำดังต่อไปนี้:

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

ทั้งสองอย่างค่อนข้างน่าเกลียด แต่จะช่วยให้คุณรู้ว่าวัตถุนั้นถูกสร้างขึ้นใหม่หรือไม่ หวังว่าจะช่วยได้!


ขณะนี้ฉันกำลังบายบั๊กในการafter_saveโทรกลับโดยใช้ Rails 4 และสิ่งเหล่านี้ไม่ได้ทำงานเพื่อระบุบันทึกใหม่นี้
MCB

ขอบอกว่า@object.previous_changes[:id].any?ค่อนข้างเรียบง่ายและดูหรูหรา มันใช้งานได้สำหรับฉันหลังจากที่บันทึกได้รับการอัปเดต (ฉันไม่ได้เรียกมันจากafter_save)
thekingoftruth

1
@object.id_previously_changed?น่าเกลียดน้อยกว่าเล็กน้อย
aNoble

@MCB ในafter_saveon Rails 4 คุณต้องการที่จะดูที่แทนchanges[:id] previous_changes[:id]สิ่งนี้กำลังเปลี่ยนแปลงใน Rails5.1 อย่างไรก็ตาม (ดูการสนทนาในgithub.com/rails/rails/pull/25337 )
gmcnaughton

1
previous_changes.key?(:id)เพื่อความเข้าใจที่ดีขึ้น
Sebastian Palma


1

สำหรับทางรถไฟ 4 (ตรวจสอบ 4.2.11.1) ผลของการchangesและprevious_changesวิธีการที่มีแฮชที่ว่างเปล่าภายในสร้างวัตถุ{} after_saveดังนั้นattribute_changed?วิธีการเช่นid_changed?จะไม่ทำงานตามที่คาดไว้

แต่คุณสามารถใช้ประโยชน์จากความรู้นี้และ - รู้ว่าต้องมีอย่างน้อย 1 แอตทริบิวต์ในการchangesอัปเดต - ตรวจสอบว่าchangesว่างเปล่า เมื่อคุณยืนยันว่าว่างเปล่าคุณต้องอยู่ระหว่างการสร้างวัตถุ:

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

0

ฉันชอบที่จะเจาะจงแม้ว่าฉันจะรู้ว่า:idไม่ควรเปลี่ยนเป็นเรื่องปกติ แต่

(byebug) id_change.first.nil?
true

มันถูกกว่าเสมอที่จะเจาะจงแทนที่จะค้นหาจุดบกพร่องที่ไม่คาดคิดแปลก ๆ

ในทำนองเดียวกันหากฉันคาดว่าจะtrueตั้งค่าสถานะจากการโต้แย้งที่ไม่น่าไว้วางใจ

def foo?(flag)
  flag == true
end

ซึ่งจะช่วยประหยัดเวลาได้มากในการไม่นั่งอยู่บนแมลงแปลก ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.