Làm thế nào để phát hiện các thay đổi thuộc tính từ mô hình?


85

Tôi muốn tạo một hàm gọi lại trong đường ray thực thi sau khi mô hình được lưu.

Tôi có mô hình này, Xác nhận quyền sở hữu có thuộc tính 'trạng thái' thay đổi tùy thuộc vào trạng thái của xác nhận quyền sở hữu, các giá trị có thể đang chờ xử lý, xác nhận, phê duyệt, bị từ chối

Cơ sở dữ liệu có 'trạng thái' với giá trị mặc định là 'đang chờ xử lý'.

Tôi muốn thực hiện các tác vụ nhất định sau khi mô hình được tạo lần đầu tiên hoặc được cập nhật từ trạng thái này sang trạng thái khác, tùy thuộc vào trạng thái đó thay đổi từ trạng thái nào.

Ý tưởng của tôi là có một chức năng trong mô hình:

    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

Câu hỏi của tôi là làm cách nào để kiểm tra giá trị trước đó trước khi thay đổi trong mô hình?

Câu trả lời:


173

Bạn nên xem ActiveModel :: Dirty module: Bạn có thể thực hiện các tác vụ sau trên mô hình Xác nhận quyền sở hữu của mình:

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"]}

Oh! niềm vui của Rails!


6
Điều này sẽ không hoạt động sau khi mô hình được lưu, đó là những gì anh ta yêu cầu.
Tom Rossi

4
@TomRossi, các dirtylệnh gọi hoạt động trong after_save(cả trong Rails 2.3 và 3.x). Tôi đã sử dụng nó vài lần.
Harish Shetty

11
@TomRossi, các cờ bẩn được đặt lại sau khi cam kết, vì vậy chúng sẽ không khả dụng trong các lệnh after_commitgọi lại được giới thiệu trong Rails 3.x. Họ chắc chắn sẽ làm việc trong after_save.
Harish Shetty

Tôi không ý kiến! Tôi nghĩ rằng chúng đã được đặt lại sau khi nó được lưu!
Tom Rossi

5
@TomRossi Tôi bắt đầu với giả định tương tự vài năm trước. Khi tôi cố gắng kiểm tra các cờ bẩn trong after_save, nó đã hoạt động. Về bản chất, after_savelà một cuộc gọi lại cho một trạng thái giữa after DMLbefore_commit. Bạn có thể chấm dứt toàn bộ giao dịch after_savebằng cách đưa ra một ngoại lệ. Nếu bạn muốn làm điều gì đó sau khi tiết kiệm với ra ảnh hưởng đến hoạt động hiện tại, sử dụng after_commit:-)
Harish Shetty

38

bạn có thể sử dụng cái này

self.changed

nó trả về một mảng tất cả các cột đã thay đổi trong bản ghi này

bạn cũng có thể dùng

self.changes

trả về một băm của các cột đã thay đổi và kết quả trước và sau dưới dạng mảng


7
Chỉ cần một lưu ý nhỏ để nói rằng không cần thiết phải sử dụng self.trên những thứ này - bạn có thể chỉ cần nói changedchanges.
user664833

@ user664833 Cụ thể hơn, bạn có thể bỏ qua selfkhi nào trong chính mô hình, nhưng bạn có thể gọi chúng trên bất kỳ đối tượng nào có object.changedobject.changes. :)
Joshua Pinter

4

Tôi khuyên bạn nên xem một trong các plugin máy trạng thái có sẵn:

Một trong hai sẽ cho phép bạn thiết lập các trạng thái và chuyển đổi giữa các trạng thái. Cách rất hữu ích và dễ dàng để xử lý các yêu cầu của bạn.


Tôi đang thử rubyist-aasm. Giả sử tôi có Class Claim <ActiveRecord :: Base bao gồm AASM aasm_column: status aasm_initial_state: pending aasm_state: pending,: enter =>: enter_pend def enter_charge Notifier.deliver_pend_notification (self) end end Và trường trạng thái trong cơ sở dữ liệu của tôi có giá trị mặc định trong tổng số "đang chờ xử lý". Nếu tôi thực hiện Claim.create mà không điền vào trường trạng thái (để nó chạy 'đang chờ xử lý'), AASM có chạy phương thức 'enter_pend' không?
David C

2

Đối với Rails 5.1+, bạn nên sử dụng phương thức thuộc tính bản ghi hoạt động: save_change_to_attribute?

save_change_to_attribute? (attr_name, ** options)

Thuộc tính này có thay đổi khi chúng tôi lưu lần cuối không? Phương thức này có thể được gọi saved_change_to_name?thay vì saved_change_to_attribute?("name"). Hành vi tương tự như attribute_changed?. Phương thức này hữu ích trong sau khi gọi lại để xác định xem lệnh gọi để lưu có thay đổi một thuộc tính nhất định hay không.

Tùy chọn

from Khi được truyền, phương thức này sẽ trả về false trừ khi giá trị ban đầu bằng với tùy chọn đã cho

to Khi được truyền, phương thức này sẽ trả về false trừ khi giá trị được thay đổi thành giá trị đã cho

Vì vậy, mô hình của bạn sẽ trông như thế này, nếu bạn muốn gọi một số phương thức dựa trên sự thay đổi trong giá trị thuộc tính:

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

Và nếu bạn không muốn kiểm tra sự thay đổi giá trị trong lệnh gọi lại, bạn có thể làm như sau ::

class Claim < ApplicationRecord

  after_save: :do_this, if: saved_change_to_status?


  def do_this
    ..
    ..
  end

end

0

Tôi đã thấy câu hỏi tăng lên ở nhiều nơi, vì vậy tôi đã viết một rubygem nhỏ cho nó, để làm cho mã đẹp hơn một chút (và tránh hàng triệu câu lệnh if / else ở khắp mọi nơi): https://github.com/ronna-s / on_change . Tôi hy vọng rằng sẽ giúp.


0

Bạn sẽ thấy tốt hơn nhiều khi sử dụng một giải pháp đã được thử nghiệm kỹ lưỡng chẳng hạn như gem state_machine .

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.