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.