Rails 3 kiểm tra nếu thuộc tính thay đổi


153

Cần kiểm tra xem một khối thuộc tính đã thay đổi trước khi cập nhật trong Rails 3.

street1, street2, thành phố, tiểu bang, mã zip

Tôi biết tôi có thể sử dụng một cái gì đó như

if @user.street1 != params[:user][:street1]
  then do something....
end

Nhưng đoạn mã đó sẽ THỰC SỰ dài. Có cách nào sạch hơn không?

Câu trả lời:


282

Kiểm tra ActiveModel :: Dirty (có sẵn trên tất cả các kiểu theo mặc định). Tài liệu này thực sự tốt, nhưng nó cho phép bạn làm những việc như:

@user.street1_changed? # => true/false

Điều này không hữu ích nếu bạn đang cố gắng xem liệu một thuộc tính đã được thay đổi trong một hình thức (có vẻ như đó là những gì OP đang làm).
Mark Fraser

8
Chắc chắn là có. Nếu bạn chỉ định các giá trị bằng cách sử dụng hàm băm params, chúng sẽ đến từ biểu mẫu.
Peter Brown

Điều gì xảy ra nếu tôi muốn làm một cái gì đó như thế này: model.collection.changed_size_by? hoặc đại loại như
Mauro Dias

@MauroDias bạn phải viết logic của riêng mình để theo dõi các thuộc tính bộ sưu tập như thế.
Peter Brown

Tôi sẽ cố gắng sử dụng một số lib thử nghiệm cho việc này. có thể là một cách giải quyết, dù sao TY
Mauro Dias

54

Đây là cách tôi giải quyết vấn đề kiểm tra các thay đổi trong nhiều thuộc tính.

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

Các changedphương thức trả về một mảng các thuộc tính thay đổi cho đối tượng đó.

Cả hai @user.changedattrslà mảng để tôi có thể có giao điểm (xem ary & other aryphương thức). Kết quả của giao điểm là một mảng. Bằng cách gọi any?vào mảng, tôi nhận được đúng nếu có ít nhất một giao lộ.

Cũng rất hữu ích, changed_attributesphương thức trả về một hàm băm của các thuộc tính với các giá trị ban đầu của chúng và changestrả về một hàm băm của các thuộc tính với các giá trị ban đầu và mới của chúng (trong một mảng).

Bạn có thể kiểm tra APIDock xem phiên bản nào hỗ trợ các phương thức này.

http://apidock.com/rails/ActiveModel/Denty


1
Đây là một cách tốt đẹp để làm điều đó. Về mặt lịch sử tôi đã thực hiện if attrs.any?{|attr| @user.send("#{attr}_changed?")}Khi tôi muốn kiểm tra xem một trong số các thuộc tính khác nhau có thay đổi hay không - Tất nhiên, tôi chỉ làm điều này với các chứng nhận mà tôi đã tự xác định, vì tôi không thích ném thông số người dùng vào một sendphương thức. ;)
nzifnab

14

ActiveModel::Dirtykhông làm việc cho tôi vì @model.update_attributes()che giấu những thay đổi. Vì vậy, đây là cách tôi phát hiện thay đổi nó trong một updatephương thức trong bộ điều khiển:

def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

Nếu bạn đang cố gắng phát hiện nhiều thay đổi thuộc tính, nó có thể trở nên lộn xộn. Có lẽ không nên làm điều này trong một bộ điều khiển, nhưng meh.


9
Đối với những gì nó có giá trị, bạn có thể làm điều này với previous_changesmặc định cũng có sẵn.
Jason Galuten

2
#update_attributeskhông che giấu những thay đổi. Nó lưu bản ghi, vì vậy mô hình được cập nhật và không có thay đổi. Bạn cần thay đổi trường trong mô hình và kiểm tra changed?trước khi lưu. I E. @ model.field = 'foo' hoặc@model.attributes = @model.attributes.merge(params[:model])
Grzegorz

9

Đối với đường ray 5.1+ cuộc gọi lại

Kể từ Ruby on Rails 5.1, các phương thức attribute_changed?attribute_wasActiveRecord sẽ không được chấp nhận

Sử dụng saved_change_to_attribute?thay vìattribute_changed?

@user.saved_change_to_street1? # => true/false

Thêm ví dụ ở đây


2

Các câu trả lời trên là tốt hơn nhưng đối với kiến ​​thức, chúng tôi cũng có một sự chấp thuận khác, giá trị cột 'catagory' đã thay đổi cho một đối tượng (@design),

@design.changes.has_key?('catagory')

.Changes sẽ trả về một hàm băm với khóa là tên và giá trị của cột dưới dạng một mảng có hai giá trị [old_value, new_value] cho mỗi cột. Ví dụ: catagory cho ở trên được thay đổi từ 'ABC' thành 'XYZ' của @design,

@design.changes   # => {} 
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }

Để tham khảo thay đổi trong ROR

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.