모델에서 속성 변경을 감지하는 방법은 무엇입니까?


85

모델이 저장된 후 실행되는 레일에 콜백 함수를 만들고 싶습니다.

이 모델에는 클레임 ​​상태에 따라 변경되는 '상태'속성이있는 클레임이 있으며 가능한 값은 보류 중, 승인 됨, 승인 됨, 거부 됨입니다.

데이터베이스에는 기본값이 '보류 중'인 '상태'가 있습니다.

모델이 처음으로 생성되거나 변경된 상태에 따라 한 상태에서 다른 상태로 업데이트 된 후 특정 작업을 수행하고 싶습니다.

내 생각은 모델에 기능을 갖는 것입니다.

    after_save :check_state

    def check_state
      # if status changed from nil to pending (created)
      do this

      # if status changed from pending to approved
      performthistask
     end

내 질문은 모델 내에서 변경하기 전에 이전 값을 어떻게 확인합니까?

답변:


173

ActiveModel :: Dirty 모듈을 살펴보아야 합니다. 클레임 모델에 대해 다음 작업을 수행 할 수 있어야합니다.

claim.status_changed?  # returns true if 'status' attribute has changed
claim.status_was       # returns the previous value of 'status' attribute
claim.status_change    # => ['old value', 'new value'] returns the old and 
                       # new value for 'status' attribute

claim.name = 'Bob'
claim.changed # => ["name"]
claim.changes # => {"name" => ["Bill", "Bob"]}

오! Rails의 즐거움!


6
그가 요청한 모델이 저장된 후에는 작동하지 않습니다.
Tom Rossi

4
@TomRossi, dirty호출은 after_saveRails 2.3 및 3.x에서 모두 작동합니다 . 나는 그것을 여러 번 사용했습니다.
Harish Shetty

11
@TomRossi, 더티 플래그는 커밋 후 재설정되므로 after_commitRails 3.x에 도입 된 콜백에서 사용할 수 없습니다 . 그들은 확실히 after_save.
Harish Shetty

나는 몰랐다! 일단 저장되면 재설정되었다고 생각했습니다!
Tom Rossi

5
@TomRossi 몇 년 전에 같은 가정으로 시작했습니다. after_save에서 더티 플래그를 확인하려고 할 때 작동했습니다. 본질적으로 와 after_save사이의 상태에 대한 콜백입니다 . 예외를 발생 시켜 전체 트랜잭션을 종료 할 수 있습니다 . 당신은 이후에 저장 현재 작업에 영향을 미치는, 사용 무언가를하고 싶은 경우 :-)after DMLbefore_commitafter_saveafter_commit
Harish 셰티

38

당신은 이것을 사용할 수 있습니다

self.changed

이 레코드에서 변경된 모든 열의 배열을 반환합니다.

당신은 또한 사용할 수 있습니다

self.changes

변경된 열의 해시와 전후 결과를 배열로 반환합니다.


7
여기에 사용할 필요가 없다는 사소한 메모 입니다. 및 self.이라고 말하면 됩니다. changedchanges
user664833

@ user664833는보다 구체적으로, 당신은 생략 할 수 있습니다 self때 모델 자체에서,하지만 당신은 어떤 객체에이를 호출 할 수 있습니다 object.changedobject.changes. :)
Joshua Pinter

4

사용 가능한 상태 머신 플러그인 중 하나를 살펴 보는 것이 좋습니다.

둘 중 하나를 통해 상태 및 상태 간 전환을 설정할 수 있습니다. 요구 사항을 처리하는 매우 유용하고 쉬운 방법입니다.


나는 rubyist-aasm을 시도하고 있습니다. Class Claim <ActiveRecord :: Base include AASM aasm_column : status aasm_initial_state : pending aasm_state : pending, : enter => : enter_pending def enter_pending Notifier.deliver_pending_notification (self) end end 그리고 데이터베이스의 내 상태 필드에 기본값이 있다고 가정 해 보겠습니다. "보류 중"입니다. 상태 필드를 채우지 않고 Claim.create를 수행하면 ( 'pending'실행) AASM이 'enter_pending'메소드를 실행합니까?
David C

2

Rails 5.1+의 경우 활성 레코드 속성 메소드를 사용해야합니다. saved_change_to_attribute?

saved_change_to_attribute? (attr_name, ** 옵션)`

마지막으로 저장했을 때이 속성이 변경 되었습니까? 이 메서드는 saved_change_to_name?대신 으로 호출 할 수 있습니다 saved_change_to_attribute?("name"). 와 유사하게 작동합니다 attribute_changed?. 이 메서드는 저장 호출이 특정 속성을 변경했는지 확인하기 위해 콜백 이후에 유용합니다.

옵션

from 전달되면이 메서드는 원래 값이 주어진 옵션과 같지 않으면 false를 반환합니다.

to 전달되면이 메서드는 값이 주어진 값으로 변경되지 않는 한 false를 반환합니다.

따라서 속성 값의 변경을 기반으로 일부 메소드를 호출하려면 모델이 다음과 같이 보일 것입니다.

class Claim < ApplicationRecord
  
  after_save :do_this, if: Proc.new { saved_change_to_status?(from: nil, to: 'pending') }

  after_save :do_that, if: Proc.new { saved_change_to_status?(from: 'pending', to: 'approved') }

  
  def do_this
    ..
    ..
  end

  def do_that
    ..
    ..
  end

end

콜백에서 값 변경을 확인하지 않으려면 다음을 수행 할 수 있습니다.

class Claim < ApplicationRecord

  after_save: :do_this, if: saved_change_to_status?


  def do_this
    ..
    ..
  end

end

0

나는 여러 곳에서 질문이 떠오르는 것을 보았으므로 코드를 조금 더 멋지게 만들기 위해 작은 루비 젬을 작성했습니다 (그리고 모든 곳에서 백만 개의 if / else 문을 피합니다) : https://github.com/ronna-s / on_change . 도움이 되었기를 바랍니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.