Rails after_save 콜백에서 변경된 속성을 결정 하시겠습니까?


153

모델의 게시 된 속성이 false에서 true로 변경된 경우에만 알림을 보내도록 모델 관찰자에서 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

경고

이 방법은 Rails 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

그냥 작동합니다.


위에서 말한 것을 잊어라-Rails 2.0.5에서는 작동하지 않습니다. Rails 3에 유용한 추가 기능
stephenr

4
after_update가 더 이상 사용되지 않는다고 생각합니까? 어쨌든, 나는 이것을 after_save 훅에서 시도했지만 정상적으로 작동하는 것 같습니다. (changes () 해시는 아직 after_save에서 아직 재설정되지 않았습니다.)
Tyler Rick

3
model.rb 파일에이 줄을 포함시켜야했습니다. ActiveModel :: Dirty 포함
coderVishal

13
이후 버전의 Rails에서는 after_update통화에 조건을 추가 할 수 있습니다 .after_update :send_notification_after_change, if: -> { published_changed? }
Koen.

11
Rails 5.2에는 일부 API 변경 사항이 있습니다. 당신은해야 할 것이다 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-previous_changes


2
모델 콜백을 사용하지 않고 추가 기능을 수행하기 전에 유효한 저장이 필요한 경우 완벽하게 작동합니다.
Dave Robertson

이것은 완벽 해요! 이것을 게시 해 주셔서 감사합니다.
jrhicks

대박! 고마워
Daniel Logan

4
after_save콜백을 사용하는 경우 self.changed?is trueand self.attribute_name_changed?is true이지만 self.previous_changes빈 해시를 반환합니다.
sandre89

9
이것은 Rails 5.1 +에서 더 이상 사용되지 않습니다. 사용 saved_changesafter_save대신 콜백
rico_mac

71

그것은 언급 할만큼 가치가이 동작이 변경 될 것입니다 : 현재 그것으로, 나중에 (8 월 2017)이 보는 사람에게 구글 꼭대기 레일 5.2 과 같이 레일 5.1로 중단 경고를 가지고 ActiveModel :: 더러운 조금 변경 .

무엇을 바꾸나요?

-callbacks attribute_changed?에서 메소드를 사용 하는 경우 다음 after_*과 같은 경고가 표시됩니다.

감가 상각 경고 : attribute_changed?다음 버전의 레일에서는 콜백 내부 동작 이 변경됩니다. 새로운 리턴 값은 save리턴 된 후 메소드를 호출하는 동작을 반영합니다 (예 : 지금 리턴하는 것과 반대). 현재 동작을 유지하려면 saved_change_to_attribute?대신 사용하십시오. (/PATH_TO/app/models/user.rb:15의 some_callback에서 호출)

언급했듯이 함수를로 바꾸면 쉽게 해결할 수 있습니다 saved_change_to_attribute?. 예를 들어, name_changed?됩니다 saved_change_to_name?.

마찬가지로,를 사용하여 attribute_change전후 값을 가져 오는 경우에도 변경되며 다음과 같은 결과가 발생합니다.

감가 상각 경고 : attribute_change다음 버전의 레일에서는 콜백 내부 동작 이 변경됩니다. 새로운 리턴 값은 save리턴 된 후 메소드를 호출하는 동작을 반영합니다 (예 : 지금 리턴하는 것과 반대). 현재 동작을 유지하려면 saved_change_to_attribute대신 사용하십시오. (/PATH_TO/app/models/user.rb:20의 some_callback에서 호출)

다시 말하지만, 메소드는 name을 saved_change_to_attribute로 변경 합니다 ["old", "new"]. 또는을 사용 saved_changes하면 모든 변경 사항이 반환되며으로 액세스 할 수 있습니다 saved_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 4에서는 콜백에 self.changed사용할 수 있습니다 after_save.
sandre89

잘 작동합니다! 또한 주목할만한 결과 self.changed는 문자열 배열입니다! (기호가 아님!)["attr_name", "other_attr_name"]
LukeS

8

"선택된"답변이 효과가 없었습니다. CouchRest :: Model (액티브 모델 기반)과 함께 레일 3.1을 사용하고 있습니다. _changed?방법은 변경 속성에 대해 true를 반환하지 않습니다 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을 사용하고 있습니다.
Ben Lee

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

이것은 매우 나쁜 습관이므로 고려하지 마십시오.
누노 실바
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.