ActiveRecord를 사용하면 after_update 중에 레코드의 이전 값을 가져올 수 있습니까?


81

간단한 예제를 사용하여 설정 : 두 번째 테이블 ( ) 에 각 레코드 Totalsamount열 합계를 보유하는 테이블 ( ) 이 1 개 Things있습니다.

thing.amount가 업데이트 되면 이전 값과 새 값의 차이를에 추가하고 싶습니다 total.sum.

지금은 뺀거야 self.amount동안 before_update및 추가 self.amountafter_update. 이로 인해 업데이트 성공에 대해 너무 많은 신뢰가 있습니다.

제약 : 모든 거래의 합계를 단순히 재 계산하고 싶지 않습니다.

질문 : 간단히 말해서 after_update콜백 중에 원래 값에 액세스하고 싶습니다 . 이 작업을 수행하는 방법은 무엇입니까?

업데이트 : 나는 Luke Francl의 아이디어로 갈 것입니다. after_update콜백 중에 self.attr_was내가 원하는 값에 여전히 액세스 할 수 있습니다 . 나는 또한 after_update이런 종류의 논리를 모델에 유지하고 싶기 때문에 구현을하기 로 결정했습니다 . 이렇게하면 나중에 트랜잭션을 어떻게 업데이트할지 결정하더라도 트랜잭션 합계를 올바르게 업데이트하고 있음을 알 수 있습니다. 구현 제안을 해주신 모든 분들께 감사드립니다.

답변:


149

모든 사람들이 거래에 대해 말하는 것과 동일합니다.

그건 ...

Rails 2.1부터 ActiveRecord는 객체의 속성 값을 추적합니다. 따라서 속성 total이 있으면 이전 값을 반환 하는 total_changed?메서드와 total_was메서드가 있습니다.

더 이상이를 추적하기 위해 모델에 아무것도 추가 할 필요가 없습니다.

업데이트 : 요청 된 ActiveModel :: Dirty에 대한 문서는 다음과 같습니다 .


이런 것이 존재한다고 생각했습니다. attr_changed에 대한 관련 문서에 링크 할 수 있습니까? 그리고 attr_was?
Gabe Hollombe

물론입니다. 답변에 대한 링크를 추가했습니다.
Luke Francl

10

다른 사람들은이 모든 것을 트랜잭션으로 감싸는 것에 대해 언급하고 있습니다.하지만 저는 그것이 당신을 위해 끝났다고 생각합니다. after_ * 콜백에서 오류에 대한 예외를 발생시켜 롤백을 트리거하기 만하면됩니다.

참조 http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html를

save, save! 또는 destroy 호출의 전체 콜백 체인은 트랜잭션 내에서 실행됩니다. 여기에는 after_ * 후크가 포함됩니다. 모든 것이 잘되면 체인이 완료되면 COMMIT가 실행됩니다.

before_ * 콜백이 작업을 취소하면 ROLLBACK이 실행됩니다. after_ * 후크를 포함한 모든 콜백에서 예외를 발생시키는 ROLLBACK을 트리거 할 수도 있습니다. 그러나이 경우 클라이언트는 일반 저장이 조용히 거짓을 반환하는 대신 이러한 예외를 발생시키기 때문에이를 인식해야합니다.



8

이전 값과 새 값을 각각 사용하여 변경된 모든 필드를 가져 오려면 다음을 수행하십시오.

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

5
나는 이제 마지막 줄이 person.previous_changes대신 읽어야한다고 믿는다person.changes
Jason Axelson

5

ActiveRecord :: Dirty 는 속성 변경을 추적하기 위해 ActiveRecord에 내장 된 모듈입니다. 따라서 thing.amount_was이전 값을 얻는 데 사용할 수 있습니다 .


3

이것을 모델에 추가하십시오.

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

그런 다음 after_update 코드에서 @old_amount를 사용합니다.


0

첫째, 데이터가 함께 작성되도록 트랜잭션에서이 작업을 수행해야합니다.

질문에 답하기 위해 before_update의 이전 값으로 멤버 변수를 설정 한 다음 after_update에서 액세스 할 수 있지만 이것은 매우 우아한 솔루션이 아닙니다.


트랜잭션을 사용하여 원하는 것을 구현하는 방법을 알아보기 위해 고심하고 있습니다. 이와 같은 트랜잭션이 모델 또는 컨트롤러에 있습니까? 'after_update'및 'before_update'콜백을 제거합니까? 마지막으로, 차이를 결정하기 위해 필요한 이전 값을 어떻게 얻습니까?
Abel

신경 쓰지 마세요, 컨트롤러에 코드를 넣어야한다는 것을 알았습니다. 우리는 좋다.
Abel

0

아이디어 1 : 데이터베이스 트랜잭션에서 업데이트를 래핑하여 업데이트가 실패하더라도 Totals 테이블이 변경되지 않도록합니다. ActiveRecord Transactions docs

아이디어 2 : before_update 중에 @old_total에 이전 값을 숨 깁니다.


0

업데이트 후 특정 필드의 값을 얻으려면 field_before_last_save 메소드를 사용할 수 있습니다.

Example:

u = User.last
u.update(name: "abc")

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