निर्धारित करें कि रेल कॉल के बाद रेल में कौन सी विशेषताएँ बदली गईं?


153

मैं अपने मॉडल ऑब्जर्वर में एक after_save कॉलबैक सेट कर रहा हूं केवल एक अधिसूचना भेजने के लिए यदि मॉडल की प्रकाशित विशेषता झूठी से सच में बदल गई थी। चूँकि तरीके बदल गए हैं? मॉडल सहेजे जाने से पहले केवल उपयोगी हैं, जिस तरह से मैं वर्तमान में (और असफल) ऐसा करने की कोशिश कर रहा हूं:

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

क्या किसी के पास इसे संभालने के सर्वोत्तम तरीके के रूप में कोई सुझाव है, अधिमानतः मॉडल ऑब्जर्वर कॉलबैक का उपयोग करके (ताकि मेरे नियंत्रक कोड को प्रदूषित न करें)?

जवाबों:


182

रेल 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

चेतावनी

यह दृष्टिकोण रेल 5.1 के माध्यम से काम करता है (लेकिन 5.1 में पदावनत है और 5.2 में परिवर्तन तोड़ रहा है)। आप इस पुल अनुरोध में परिवर्तन के बारे में पढ़ सकते हैं ।

after_updateमॉडल पर अपने फ़िल्टर में आप एक्सेसर का उपयोग कर सकते हैं _changed?। उदाहरण के लिए:

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

यह सिर्फ काम करता है।


भूल जाओ कि मैंने ऊपर क्या कहा था - यह 2.0.5 रेल में काम नहीं करता है। तो रेल 3. के लिए एक उपयोगी इसके अलावा
स्टीफन

4
मुझे लगता है कि after_update अब पदावनत हो गया है? वैसे भी, मैं एक after_save हुक में यह कोशिश की और यह ठीक काम करने के लिए लग रहा था। (परिवर्तन () हैश अभी भी एक after_save, जाहिरा तौर पर अभी तक रीसेट नहीं किया गया है।)
टायलर रिक

3
मुझे इस लाइन को model.rb फ़ाइल में शामिल करना था। ActiveModel :: डर्टी
कोडरविशाल

13
रेल के बाद के संस्करणों में आप after_updateकॉल में शर्त जोड़ सकते हैं :after_update :send_notification_after_change, if: -> { published_changed? }
कोएन।

11
रेल 5.2 में कुछ एपीआई परिवर्तन होंगे। आपको कॉलबैक के दौरान परिवर्तन करना होगा saved_change_to_published?या करना होगाsaved_change_to_published
alopez02

183

उन लोगों के लिए जो केवल after_saveकॉलबैक में किए गए परिवर्तनों को जानना चाहते हैं :

5.1 और अधिक से अधिक रेल

model.saved_changes

रेल्स <5.1

model.previous_changes

इसे भी देखें: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-prepret_changes


2
यह पूरी तरह से काम करता है जब आप मॉडल कॉलबैक का उपयोग नहीं करना चाहते हैं और आगे की कार्यक्षमता को आगे बढ़ाने से पहले एक वैध बचत की आवश्यकता होती है।
डेव रॉबर्टसन

यह पूर्ण है! इसे पोस्ट करने के लिए आपका धन्यवाद।
jrhicks

बहुत बढ़िया! इसके लिए धन्यवाद।
डैनियल लोगान

4
बस स्पष्ट होना: मेरे परीक्षणों में (रेल 4), यदि आप एक का उपयोग कर रहे after_saveकॉलबैक, self.changed?है trueऔर self.attribute_name_changed?भी है true, लेकिन self.previous_changesएक खाली हैश देता है।
सैंड्रे 89

9
यह रेल 5.1 + में पदावनत है। इसके बजाय कॉलबैक saved_changesमें उपयोग करेंafter_save
rico_mac

71

बाद में इसे देखने वाले किसी भी व्यक्ति के लिए, क्योंकि यह वर्तमान में (अगस्त 2017) गूगल में सबसे ऊपर है: यह ध्यान देने योग्य है, कि इस व्यवहार को रेल 5.2 में बदल दिया जाएगा , और इसमें रीले 5.1 के रूप में सक्रियकरण चेतावनी है, जैसे कि ActiveModel - डर्टी थोड़ा बदल गया है ।

मैं क्या बदलूं?

यदि आप -allallbacks attribute_changed?में विधि का उपयोग कर रहे हैं after_*, तो आपको एक चेतावनी दिखाई देगी जैसे:

समीक्षा चेतावनी: attribute_changed?कॉलबैक के बाद के अंदर का व्यवहार रेल के अगले संस्करण में बदल जाएगा। नया रिटर्न वैल्यू वापस आने के बाद विधि को कॉल करने के व्यवहार को प्रतिबिंबित करेगा 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कॉलबैक के बाद के अंदर का व्यवहार रेल के अगले संस्करण में बदल जाएगा। नया रिटर्न वैल्यू रिटर्न के बाद विधि को कॉल करने के व्यवहार को प्रतिबिंबित करेगा save(उदाहरण के लिए जो अब लौटता है उसके विपरीत)। वर्तमान व्यवहार को बनाए रखने के लिए, saved_change_to_attributeइसके बजाय का उपयोग करें । (some_callback से /PATH_TO/app/models/user.rb:20 पर कॉल किया गया)

फिर, जैसा कि यह उल्लेख करता है, विधि नाम बदलकर saved_change_to_attributeकिस पर लौटती है ["old", "new"]। या उपयोग saved_changes, जो सभी परिवर्तन लौटाता है, और इन्हें इस रूप में एक्सेस किया जा सकता है saved_changes['attribute']


2
ध्यान दें कि इस उपयोगी प्रतिक्रिया में attribute_wasविधियों के पदावनत के लिए वर्कअराउंड भी शामिल है : saved_change_to_attributeइसके बजाय उपयोग करें ।
जो एटजबर्गर

47

यदि आप इसके before_saveबजाय ऐसा कर सकते हैं after_save, तो आप इसका उपयोग कर सकेंगे:

self.changed

यह इस रिकॉर्ड में सभी परिवर्तित स्तंभों की एक सरणी देता है।

आप भी उपयोग कर सकते हैं:

self.changes

जो स्तंभों का एक हैश लौटाता है और पहले और बाद में परिणाम के रूप में बदल जाता है


8
सिवाय इसके कि ये after_कॉलबैक में काम नहीं करते हैं , जो वास्तव में सवाल था। @ jacek-głodek का उत्तर सही है।
जैज

यह स्पष्ट करने के लिए उत्तर को अपडेट करें कि यह केवल लागू होता हैbefore_save
mahemoff

1
यह कौन सा रेल संस्करण है? रेल 4 में, कॉलबैक self.changedमें उपयोग किया जा सकता है after_save
सैंड्रे89

बहुत अच्छा काम करता है! यह भी ध्यान दिया जाना चाहिए, इसका परिणाम self.changedस्ट्रिंग्स की एक सरणी है! (प्रतीक नहीं!)["attr_name", "other_attr_name"]
लूकास

8

"चयनित" उत्तर मेरे काम नहीं आया। मैं CouchRest के साथ रेल का उपयोग कर रहा हूँ 3.1 :: मॉडल (सक्रिय मॉडल पर आधारित)। _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

आप बस एक एक्सेसर जोड़ते हैं जो परिभाषित करते हैं कि आप क्या बदलते हैं

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

यह एक बहुत बुरा अभ्यास है, इस पर भी विचार न करें।
नूनो सिल्वा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.