Sử dụng ActiveRecord, có cách nào để lấy các giá trị cũ của bản ghi trong after_update


81

Thiết lập bằng một ví dụ đơn giản: Tôi có 1 bảng ( Totals) chứa tổng amountcột của mỗi bản ghi trong bảng thứ hai ( Things).

Khi một 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.

Ngay bây giờ tôi đang trừ self.amounttrong before_updatevà cộng self.amounttrong after_update. Điều này đặt WAY quá nhiều tin tưởng vào việc cập nhật thành công.

Hạn chế: Tôi không muốn chỉ đơn giả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 after_updategọi lại. 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 after_updategọi lại, bạn vẫn có quyền truy cập vào các self.attr_wasgiá trị chính xác là những gì tôi muốn. Tôi cũng quyết định after_updatethực hiện 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 các 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 chính xác tổng các giao dịch. Cảm ơn tất cả mọi người cho đề xuất thực hiện của bạn.

Câu trả lời:


148

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

Mà nói...

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

Không cần 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.


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?
Gabe Hollombe ngày

Chắc chắn, tôi đã thêm một liên kết đến câu trả lời.
Luke Francl ngày

10

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 đó đã hoàn thành cho bạn; bạn chỉ cần kích hoạt khôi phục bằng cách nêu ra một 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 gọi lại của một cuộc gọi lưu, lưu! Hoặc hủy chạy trong một giao dịch. Điều đó bao gồm các móc sau_ *. Nếu mọi thứ suôn sẻ, một COMMIT sẽ được thực hiện sau khi chuỗi đã hoàn thành.

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


10

Việc thêm "_was" vào 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.

Những phương pháp này được gọi là phương pháp bẩn .

Chúc mừng!


8

Để nhận tất cả các trường đã thay đổi, với giá trị cũ và mới của chúng tương ứng:

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

5
Tôi tin rằng dòng cuối cùng bây giờ nên đọc person.previous_changesthay vìperson.changes
Jason Axelson

5

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


3

Thêm cái 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.


0

Đầu tiên, bạn nên thực hiện điều này trong một giao dịch để đảm bảo rằng dữ liệu của bạn được ghi cùng nhau.

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


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

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

0

Ý tưởng 1: Kết hợp cập nhật trong một giao dịch cơ sở dữ liệu, để nếu cập nhật không thành công, bảng Tổng số của bạn sẽ không thay đổi: Tài liệu về Giao dịch ActiveRecord

Ý tưởng 2: Lưu trữ giá trị cũ trong @old_total trong before_update.


0

Nếu bạn muốn nhận giá trị của trường cụ thể sau khi cập nhật, bạn có thể sử dụng phương thức 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.