ตรวจสอบว่ามีการเปลี่ยนแปลงคุณสมบัติอะไรบ้างใน Rails after_save callback?


153

ฉันตั้งค่าการโทรกลับ after_save ในผู้สังเกตการณ์แบบจำลองของฉันเพื่อส่งการแจ้งเตือนเฉพาะเมื่อแอตทริบิวต์ที่เผยแพร่ของโมเดลนั้นเปลี่ยนจาก false เป็น true ตั้งแต่วิธีการเช่นการเปลี่ยนแปลง? มีประโยชน์เฉพาะก่อนที่แบบจำลองจะได้รับการบันทึกวิธีที่ฉันกำลัง (และไม่สำเร็จ) ในขณะนี้ที่พยายามทำมีดังนี้:

def before_save(blog)
  @og_published = blog.published?
end

def after_save(blog)
  if @og_published == false and blog.published? == true
    Notification.send(...)
  end
end

ไม่มีใครมีข้อเสนอแนะใด ๆ เกี่ยวกับวิธีที่ดีที่สุดในการจัดการสิ่งนี้โดยเฉพาะอย่างยิ่งการใช้ callbacks ผู้สังเกตการณ์แบบจำลอง (เพื่อไม่ให้เกิดมลภาวะรหัสควบคุม)

คำตอบ:


182

Rails 5.1+

ใช้saved_change_to_published?:

class SomeModel < ActiveRecord::Base
  after_update :send_notification_after_change

  def send_notification_after_change
    Notification.send(…) if (saved_change_to_published? && self.published == true)
  end

end

saved_change_to_attribute?(:published)หรือถ้าคุณต้องการ

ราง 3–5.1

คำเตือน

วิธีการนี้ใช้งานได้กับ Rails 5.1 (แต่ไม่รองรับใน 5.1 และแบ่งการเปลี่ยนแปลงใน 5.2) คุณสามารถอ่านเกี่ยวกับการเปลี่ยนแปลงในคำขอดึงนี้

ในafter_updateตัวกรองของคุณในรุ่นที่คุณสามารถใช้_changed?accessor ตัวอย่างเช่น:

class SomeModel < ActiveRecord::Base
  after_update :send_notification_after_change

  def send_notification_after_change
    Notification.send(...) if (self.published_changed? && self.published == true)
  end

end

มันใช้งานได้


ลืมสิ่งที่ฉันพูดไปข้างต้น - มันไม่ทำงานใน Rails 2.0.5 นอกจากนี้ยังมีประโยชน์สำหรับ Rails 3
stephenr

4
ฉันคิดว่า after_update เลิกใช้แล้วหรือยัง อย่างไรก็ตามฉันลองใช้เบ็ด after_save และดูเหมือนว่าจะทำงานได้ดี (การเปลี่ยนแปลง () แฮยังไม่ได้รับการรีเซ็ตใน after_save, apparently.)
Tyler Rick

3
ฉันต้องรวมบรรทัดนี้ไว้ในไฟล์ model.rb รวม ActiveModel :: Dirty
coderVishal

13
ใน Rails รุ่นที่ใหม่กว่าคุณสามารถเพิ่มเงื่อนไขในการafter_updateโทร:after_update :send_notification_after_change, if: -> { published_changed? }
Koen

11
จะมีการเปลี่ยนแปลง API บางอย่างใน Rails 5.2 คุณจะต้องทำsaved_change_to_published?หรือsaved_change_to_publishedดึงข้อมูลการเปลี่ยนแปลงในระหว่างการโทรกลับ
alopez02

183

สำหรับผู้ที่ต้องการทราบความเปลี่ยนแปลงที่เกิดขึ้นในการafter_saveโทรกลับ:

Rails 5.1 และสูงกว่า

model.saved_changes

Rails <5.1

model.previous_changes

ดูเพิ่มเติมที่: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes


2
สิ่งนี้ทำงานได้อย่างสมบูรณ์เมื่อคุณไม่ต้องการใช้การเรียกกลับแบบและต้องการการบันทึกที่ถูกต้องก่อนที่จะดำเนินการฟังก์ชันการทำงานเพิ่มเติม
Dave Robertson

มันสมบูรณ์แบบ! ขอขอบคุณสำหรับการโพสต์นี้.
jrhicks

! น่ากลัว ขอบคุณสำหรับสิ่งนี้.
Daniel Logan

4
เพียงเพื่อให้ชัดเจน: ในการทดสอบของฉัน (Rails 4) หากคุณใช้การafter_saveติดต่อกลับself.changed?เป็นtrueและself.attribute_name_changed?ยังtrueแต่self.previous_changesกลับแฮชว่างเปล่า
sandre89

9
สิ่งนี้เลิกใช้แล้วใน Rails 5.1 + ใช้saved_changesในการafter_saveโทรกลับแทน
rico_mac

71

สำหรับทุกคนที่เห็นสิ่งนี้ในภายหลังในขณะนี้ (สิงหาคม 2017) ติดอันดับ google: มันเป็นสิ่งที่ควรค่าแก่การกล่าวถึงพฤติกรรมนี้จะเปลี่ยนแปลงในRails 5.2และมีคำเตือนเลิกใช้งาน Rails 5.1 เนื่องจากActiveModel :: Dirtyเปลี่ยนไปเล็กน้อย .

ฉันจะเปลี่ยนอะไร

หากคุณใช้attribute_changed?วิธีการในการafter_*โทรกลับคุณจะเห็นคำเตือนดังนี้

คำเตือนการattribute_changed?เลิกใช้งาน: พฤติกรรมของภายในจากหลังการเรียกกลับจะมีการเปลี่ยนแปลงใน Rails รุ่นถัดไป ค่าตอบแทนใหม่จะสะท้อนถึงพฤติกรรมของการเรียกวิธีการหลังจากsaveกลับมา (เช่นตรงข้ามกับสิ่งที่มันกลับมาตอนนี้) เพื่อรักษาพฤติกรรมปัจจุบันให้ใช้saved_change_to_attribute?แทน (เรียกจาก some_callback ที่ /PATH_TO/app/models/user.rb:15)

saved_change_to_attribute?ในขณะที่มันกล่าวถึงคุณสามารถแก้ไขปัญหานี้ได้อย่างง่ายดายโดยการเปลี่ยนฟังก์ชั่นที่มี ดังนั้นสำหรับตัวอย่างเช่นจะกลายเป็นname_changed?saved_change_to_name?

ในทำนองเดียวกันหากคุณกำลังใช้attribute_changeเพื่อรับค่าก่อนหน้าการเปลี่ยนแปลงนี้จะดำเนินการเช่นกันและส่งต่อไปนี้:

คำเตือนการattribute_changeเลิกใช้งาน: พฤติกรรมของภายในจากหลังการเรียกกลับจะมีการเปลี่ยนแปลงใน Rails รุ่นถัดไป ค่าตอบแทนใหม่จะสะท้อนถึงพฤติกรรมของการเรียกวิธีการหลังจากsaveกลับมา (เช่นตรงข้ามกับสิ่งที่มันกลับมาตอนนี้) เพื่อรักษาพฤติกรรมปัจจุบันให้ใช้saved_change_to_attributeแทน (เรียกจาก some_callback ที่ /PATH_TO/app/models/user.rb:20)

อีกครั้งในขณะที่มันกล่าวถึงวิธีการเปลี่ยนชื่อไปซึ่งผลตอบแทนsaved_change_to_attribute ["old", "new"]หรือการใช้งานซึ่งจะส่งกลับการเปลี่ยนแปลงทั้งหมดและสิ่งเหล่านี้สามารถเข้าถึงได้เป็นsaved_changessaved_changes['attribute']


2
โปรดทราบว่าการตอบสนองที่เป็นประโยชน์นี้ยังรวมถึงการแก้ไขปัญหาสำหรับการคัดค้านattribute_wasวิธีการ: ใช้saved_change_to_attributeแทน
Joe Atzberger

47

ในกรณีที่คุณสามารถทำได้before_saveแทนคุณafter_saveจะสามารถใช้สิ่งนี้:

self.changed

มันส่งกลับอาร์เรย์ของคอลัมน์ที่เปลี่ยนแปลงทั้งหมดในบันทึกนี้

คุณยังสามารถใช้:

self.changes

ซึ่งจะส่งกลับค่าแฮชของคอลัมน์ที่เปลี่ยนแปลงและก่อนและหลังผลลัพธ์เป็นอาร์เรย์


8
ยกเว้นว่าสิ่งเหล่านี้ใช้ไม่ได้กับการafter_ติดต่อกลับซึ่งเป็นคำถามที่เกี่ยวกับ @ jacek-głodekคำตอบด้านล่างเป็นคำตอบที่ถูกต้อง
แจ๊ส

อัปเดตคำตอบเพื่อให้ชัดเจนเพียงใช้กับbefore_save
mahemoff

1
เวอร์ชั่น Rails นี้เป็นรุ่นอะไร? ใน Rails 4 self.changedสามารถใช้ในการafter_saveเรียกกลับ
sandre89

ใช้งานได้ดี! นอกจากนี้ยังจะต้องสังเกตผลของself.changedการเป็นอาร์เรย์ของสตริง! (ไม่ใช่สัญลักษณ์!)["attr_name", "other_attr_name"]
Luke

8

คำตอบ "เลือก" ไม่ทำงานสำหรับฉัน ฉันใช้ rails 3.1 กับ CouchRest :: Model (อิงกับ Active Model) _changed?วิธีการไม่ได้กลับจริงสำหรับแอตทริบิวต์การเปลี่ยนแปลงในafter_updateเบ็ดเฉพาะในbefore_updateเบ็ด ฉันสามารถทำให้มันทำงานได้โดยใช้around_updateตะขอ(ใหม่?) :

class SomeModel < ActiveRecord::Base
  around_update :send_notification_after_change

  def send_notification_after_change
    should_send_it = self.published_changed? && self.published == true

    yield

    Notification.send(...) if should_send_it
  end

end

1
คำตอบที่เลือกยังไม่ได้ผลสำหรับฉัน แต่สิ่งนี้ทำ ขอบคุณ! ฉันใช้ ActiveRecord 3.2.16
เบ็นลี

5

คุณสามารถเพิ่มเงื่อนไขในสิ่งที่after_updateชอบได้:

class SomeModel < ActiveRecord::Base
  after_update :send_notification, if: :published_changed?

  ...
end

ไม่จำเป็นต้องเพิ่มเงื่อนไขภายในsend_notificationเมธอดเอง


-17

คุณเพียงแค่เพิ่ม accessor ที่กำหนดสิ่งที่คุณเปลี่ยนแปลง

class Post < AR::Base
  attr_reader :what_changed

  before_filter :what_changed?

  def what_changed?
    @what_changed = changes || []
  end

  after_filter :action_on_changes

  def action_on_changes
    @what_changed.each do |change|
      p change
    end
  end
end

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