2009-03-03 23 views
60

Thiết lập sử dụng một ví dụ đơn giản: Tôi đã có 1 bảng (Totals) chứa tổng các amount cột của mỗi bản ghi trong một bảng thứ hai (Things).Sử dụng ActiveRecord, là có một cách để có được các giá trị cũ của một kỷ lục trong after_update

Khi số thing.amount được cập nhật, tôi chỉ muốn thêm sự khác biệt giữa giá trị cũ và giá trị mới vào total.sum.

Hiện tại tôi đang trừ self.amount trong khi before_update và thêm self.amount trong thời gian after_update. Điều này đặt WAY quá nhiều niềm tin vào bản cập nhật thành công.

Hạn chế: Tôi không muốn tính toán lại tổng của tất cả các giao dịch.

Câu hỏi: Rất đơn giản, tôi muốn truy cập giá trị ban đầu trong khi gọi lại after_update. Bạn đã nghĩ ra cách nào để làm điều này?

Cập nhật: Tôi sẽ theo ý tưởng của Luke Francl. Trong khi gọi lại số after_update, bạn vẫn có quyền truy cập vào các giá trị self.attr_was chính xác là những gì tôi muốn. Tôi cũng quyết định thực hiện after_update vì tôi muốn giữ loại logic này trong mô hình. Bằng cách này, bất kể tôi quyết định cập nhật giao dịch trong tương lai như thế nào, tôi sẽ biết rằng tôi đang cập nhật tổng số giao dịch một cách chính xác. Cảm ơn mọi người vì đề xuất triển khai của bạn.

Trả lời

124

Ditto mọi người đang nói gì về giao dịch.

Điều đó nói rằng ...

ActiveRecord như của Rails 2.1 theo dõi các giá trị thuộc tính của đối tượng. Vì vậy, nếu bạn có thuộc tính total, bạn sẽ có phương thức total_changed? và phương thức total_was trả về giá trị cũ.

Không cần phải thêm bất kỳ thứ gì vào mô hình của bạn để theo dõi điều này nữa.

Cập nhật: Đây là tài liệu cho ActiveModel::Dirty theo yêu cầu.

+0

Tôi nghĩ một cái gì đó như thế này tồn tại. Bạn có thể liên kết đến tài liệu liên quan về attr_changed không? và attr_was? –

+0

Chắc chắn, tôi đã thêm một liên kết vào câu trả lời. –

0

Thứ nhất, bạn nên thực hiện điều này trong giao dịch để đảm bảo dữ liệu của bạn được viết cùng nhau.

Để trả lời câu hỏi, bạn chỉ có thể đặt biến thành viên thành giá trị cũ trong before_update, sau đó bạn có thể truy cập vào after_update, tuy nhiên đây không phải là giải pháp rất thanh lịch.

+0

Tôi đang đấu tranh để xem cách tôi sẽ triển khai những gì tôi muốn sử dụng giao dịch. Có một giao dịch như thế này sống trong mô hình hoặc trong bộ điều khiển không? Tôi có xóa các cuộc gọi lại 'after_update' và 'before_update' của mình không? Cuối cùng, làm thế nào để tôi có được giá trị cũ mà tôi cần để xác định sự khác biệt? – Abel

+0

nevermind, tôi thấy rằng tôi phải đặt mã trong bộ điều khiển. là tốt. – Abel

0

Idea 1: Gói cập nhật trong một giao dịch cơ sở dữ liệu, do đó nếu bản cập nhật thất bại Totals bạn bảng là không thay đổi: ActiveRecord Transactions docs

Idea 2: stash giá trị cũ trong @old_total trong before_update.

2

Thêm phần này vào mô hình của bạn:

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

Sau đó sử dụng @old_amount trong mã after_update của bạn.

8

Một số người khác đang đề cập đến việc gói tất cả những điều này trong một giao dịch, nhưng tôi nghĩ điều đó được thực hiện cho bạn; bạn chỉ cần kích hoạt khôi phục bằng cách tăng ngoại lệ cho các lỗi trong lệnh gọi lại after_ *.

Xem http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Toàn bộ chuỗi callback của một tiết kiệm, tiết kiệm !, hoặc phá hủy cuộc gọi chạy trong một giao dịch. Điều đó bao gồm after_ * hooks. Nếu mọi thứ diễn ra tốt đẹp thì một COMMIT được thực thi khi chuỗi đã được hoàn thành.

Nếu cuộc gọi before_ * hủy bỏ hành động mà ROLLBACK được phát hành. Bạn cũng có thể kích hoạt một ROLLBACK nâng cao một ngoại lệ trong bất kỳ callback nào, bao gồm cả after_ * hooks. Lưu ý, tuy nhiên, trong trường hợp đó khách hàng cần phải nhận thức được nó vì một lưu thông thường sẽ tăng ngoại lệ như vậy thay vì lặng lẽ trở về sai.

4

ActiveRecord::Dirty là mô-đun được tích hợp vào ActiveRecord để theo dõi thay đổi thuộc tính. Vì vậy, bạn có thể sử dụng thing.amount_was để nhận giá trị cũ.

6

Để có được tất cả các lĩnh vực thay đổi, với các giá trị cũ và mới của họ lần lượt là:

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

Tôi tin rằng dòng cuối cùng sẽ đọc 'person.previous_changes' thay vì' person.changes' –

5

Phụ thêm "_was" để thuộc tính của bạn sẽ cung cấp cho bạn giá trị trước đó trước khi lưu dữ liệu.

Các phương pháp này được gọi là phương pháp dirty methods.

Chúc mừng!

Các vấn đề liên quan