Call_save geri çağırma işleminden sonra Rails'te hangi özniteliklerin değiştirildiğini belirleme?


153

Model gözlemcisinde yalnızca modelin yayımlanan özniteliği yanlıştan doğruya değiştirildiyse bir bildirim göndermek için bir after_save geri çağırma ayarlıyorum . Gibi yöntemler değişti beri ? yalnızca model kaydedilmeden önce yararlıdır, şu anda (ve başarısız) bunu yapmaya çalıştığım yol aşağıdaki gibidir:

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

Tercihen model gözlemci geri çağrıları kullanarak (denetleyici kodumu kirletmemek için) bunu idare etmenin en iyi yolu hakkında herhangi bir önerisi olan var mı?

Yanıtlar:


182

Raylar 5.1+

Kullanım 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

Veya isterseniz saved_change_to_attribute?(:published).

Raylar 3–5,1

Uyarı

Bu yaklaşım, Rails 5.1 ile çalışır (ancak 5.1'de kullanımdan kaldırılmıştır ve 5.2'de değişikliklere sahiptir). Bu çekme isteğinde değişiklik hakkında bilgi edinebilirsiniz .

Sizin de after_updatemodel üzerinde filtrenin sen yapabilirsiniz kullanmak _changed?erişimci. Yani mesela:

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

Sadece çalışıyor.


Yukarıda söylediğimi unutun - Rails 2.0.5'te çalışmaz. Yani Rails 3 için yararlı bir ek
— stephenr

4
Bence after_update şimdi kullanımdan kaldırıldı mı? Neyse, bunu bir after_save kanca denedim ve bu iyi çalışıyor gibi görünüyordu. (Değişiklik () karması hala görünüşte bir after_save'da henüz sıfırlanmadı.)
— Tyler Rick

3
Bu satırı model.rb dosyasına eklemek zorunda kaldım. include ActiveModel :: Kirli
— kodlayıcıVishal

13
Rails'in sonraki sürümlerinde after_updateçağrıyı koşullara ekleyebilirsiniz :after_update :send_notification_after_change, if: -> { published_changed? }
— Koen.

11
Rails 5.2'de bazı API değişiklikleri olacak. Geri arama sırasında değişikliği yapmanız saved_change_to_published?veya almanız gerekeceksaved_change_to_published
— alopez02

183

Yeni yapılan değişiklikleri bilmek isteyenler için after_save geri aramada :

Raylar 5.1 ve üstü

model.saved_changes

Raylar <5.1

model.previous_changes

Ayrıca bkz: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes


2
Model geri çağrılarını kullanmak istemediğinizde ve daha fazla işlevsellik gerçekleştirmeden önce geçerli bir kaydetmeye ihtiyaç duyduğunuzda bu mükemmel çalışır.
— Dave Robertson

Bu harika! Bunu gönderdiğiniz için teşekkür ederiz.
— jrhicks

Müthiş! Bunun için teşekkürler.
— Daniel Logan

4
Açık olmak gerekirse: Testlerimde (Rails 4), bir after_savegeri arama kullanıyorsanız self.changed?, trueve self.attribute_name_changed?aynı zamanda true, ancak self.previous_changesboş bir karma döndürür.
— sandre89

9
Bu, Rails 5.1 + 'da kullanımdan kaldırılmıştır. Kullanım saved_changesiçinde after_saveyerine geri aramaları
— rico_mac

71

Bunu daha sonra gören herkese, şu anda (Ağustos 2017) Google'ın başında geliyor: Bu davranışın Rails 5.2'de değiştirileceğini ve Rails 5.1'den itibaren ActiveModel :: Dirty olarak kullanımdan kaldırma uyarılarına sahip olduğunu belirtmek gerekir. biraz değişti .

Neyi değiştireceğim?

-Callbacks attribute_changed?yöntemini kullanıyorsanız after_*, aşağıdaki gibi bir uyarı görürsünüz:

DEPRECATION UYARI: attribute_changed?Rails'in sonraki sürümünde geri arama sonrası içlerin davranışı değişecektir. Yeni dönüş değeri, savedöndürüldükten sonra yöntemin çağrılması davranışını yansıtır (örneğin, şimdi döndürdüğünün tersi). Geçerli davranışı korumak için saved_change_to_attribute?bunun yerine kullanın. (/PATH_TO/app/models/user.rb:15 adresindeki some_callback adresinden çağrılır)

Bahsettiği gibi, bunu işlevle değiştirerek kolayca düzeltebilirsiniz saved_change_to_attribute?. Örneğin, name_changed?olur saved_change_to_name?.

Benzer şekilde, attribute_changeönceki değerleri almak için de kullanıyorsanız , bu da değişir ve aşağıdakileri atar:

DEPRECATION UYARI: attribute_changeRails'in sonraki sürümünde geri arama sonrası içlerin davranışı değişecektir. Yeni dönüş değeri, savedöndürüldükten sonra yöntemin çağrılması davranışını yansıtır (örneğin, şimdi döndürdüğünün tersi). Geçerli davranışı korumak için saved_change_to_attributebunun yerine kullanın. (/PATH_TO/app/models/user.rb:20 adresindeki some_callback adresinden çağrılır)

Yine, belirtildiği gibi, yöntem saved_change_to_attributedöndürülen adı değiştirir ["old", "new"]. veya kullanın saved_changes, bu da tüm değişiklikleri döndürür ve bunlara şu şekilde erişilebilir saved_changes['attribute'].


2
Bu yararlı yanıtın ayrıca attribute_wasyöntemlerin kullanımdan kaldırılması için geçici çözümler içerdiğini unutmayın : saved_change_to_attributebunun yerine use .
— Joe Atzberger

47

Bunu before_savebunun yerine yapabiliyorsanız after_save, bunu kullanabilirsiniz:

self.changed

bu kayıttaki tüm değiştirilmiş sütunların bir dizisini döndürür.

Ayrıca kullanabilirsiniz:

self.changes

değiştirilen ve sonuçlardan önce ve sonra diziler olarak bir sütun karma döndürür


8
Bunların after_geri aramada işe yaramaması dışında , sorunun asıl nedeni budur. @ jacek-głodek'in cevabı doğru.
— Jazz

Bunun sadece aşağıdakiler için geçerli olduğunu açıklığa kavuşturacak yanıt güncellendibefore_save
— mahemoff

1
Bu hangi Rails versiyonu? Rails 4'te geri aramalarda self.changedkullanılabilir after_save.
— sandre89

Harika çalışıyor! Ayrıca dikkat edilmesi gereken self.changed, dizelerin bir dizisidir! (Semboller değil!)["attr_name", "other_attr_name"]
— LukeS

8

"Seçilen" cevap benim için çalışmadı. Rauch 3.1 CouchRest :: Model (Active Model dayalı) kullanıyorum. _changed?Yöntemler değişti nitelikler için gerçek dönmüyor after_updatesadece, kanca before_updatekanca. Ben (yeni?) around_updateKanca kullanarak işe almak başardı :

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
Seçilen cevap benim için de işe yaramadı, ama bu işe yaradı. Teşekkürler! ActiveRecord 3.2.16 kullanıyorum.
— Ben Lee

5

after_updatebenzer bir koşul ekleyebilirsiniz :

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

  ...
end

send_notificationyöntemin içine bir koşul eklemeye gerek yoktur .


-17

Sadece neyi değiştirdiğinizi tanımlayan bir erişimci eklersiniz

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

Bu çok kötü bir uygulamadır, düşünmeyin bile.
— Nuno Silva
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.