ราง 3 ตรวจสอบว่ามีการเปลี่ยนแปลงคุณลักษณะ


153

ต้องตรวจสอบว่ามีการเปลี่ยนแปลงกลุ่มของคุณลักษณะก่อนที่จะอัพเดทใน Rails 3

ถนน 1, ถนน 2, เมือง, รัฐ, รหัสไปรษณีย์

ฉันรู้ว่าฉันสามารถใช้สิ่งที่ชอบ

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

แต่โค้ดนั้นจะยาวมาก ๆ มีวิธีที่สะอาดกว่านี้ไหม?

คำตอบ:


282

ลองใช้ActiveModel :: Dirty (ใช้ได้กับทุกรุ่นโดยค่าเริ่มต้น) เอกสารเป็นสิ่งที่ดี แต่ช่วยให้คุณทำสิ่งต่าง ๆ เช่น:

@user.street1_changed? # => true/false

สิ่งนี้ไม่ได้ช่วยหากคุณพยายามที่จะดูว่ามีการเปลี่ยนแปลงคุณสมบัติในรูปแบบหรือไม่ (ซึ่งน่าจะเป็นสิ่งที่ OP กำลังทำอยู่)
Mark Fraser

8
แน่นอนค่ะ หากคุณกำหนดค่าโดยใช้แฮชพารามิเตอร์เหล่านี้มาจากแบบฟอร์ม
ปีเตอร์บราวน์

ถ้าฉันต้องการทำสิ่งนี้: model.collection.changed_size_by? หรือบางอย่างเช่น
Mauro Dias

@MauroDias คุณจะต้องเขียนตรรกะของคุณเองเพื่อติดตามคุณสมบัติการรวบรวมเช่นนั้น
ปีเตอร์บราวน์

ฉันจะลองใช้ทดสอบ lib สำหรับเรื่องนี้ อาจเป็นการหลีกเลี่ยงปัญหา TY
Mauro Dias

54

นี่คือวิธีที่ฉันแก้ไขปัญหาการตรวจสอบการเปลี่ยนแปลงในหลายคุณลักษณะ

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

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

changedวิธีการส่งกลับอาร์เรย์ของคุณลักษณะที่มีการเปลี่ยนแปลงสำหรับวัตถุที่

ทั้งสอง@user.changedและattrsเป็นอาร์เรย์ดังนั้นฉันจะได้จุดตัด (ดูary & other aryวิธีการ) ผลลัพธ์ของการแยกเป็นอาร์เรย์ โดยการโทรany?อาร์เรย์ฉันจะได้รับจริงถ้ามีสี่แยกอย่างน้อยหนึ่ง

นอกจากนี้ยังมีประโยชน์มากchanged_attributesเมธอดส่งคืนแฮชของแอททริบิวด้วยค่าดั้งเดิมและchangesแฮชของแอททริบิวด้วยค่าดั้งเดิมและค่าใหม่ (ในอาร์เรย์)

คุณสามารถตรวจสอบ APIDock ว่าเวอร์ชันใดรองรับวิธีการเหล่านี้

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


1
นี่เป็นวิธีที่ดีในการทำมัน ประวัติศาสตร์ฉันทำไปแล้วif attrs.any?{|attr| @user.send("#{attr}_changed?")}เมื่อฉันต้องการตรวจสอบว่ามีหนึ่งในคุณลักษณะที่แตกต่างกันหลายอย่างเปลี่ยนไป - แน่นอนฉันแค่ทำสิ่งนี้ด้วย attrs ที่ฉันได้กำหนดตัวเองไว้เพราะฉันไม่ชอบให้ผู้ใช้แบ่งพารามิเตอร์เป็นsendวิธี ;)
nzifnab

14

ActiveModel::Dirtyไม่ได้ผลสำหรับฉันเพราะ@model.update_attributes()ซ่อนการเปลี่ยนแปลง ดังนั้นนี่คือวิธีที่ฉันตรวจพบการเปลี่ยนแปลงในupdateวิธีการในคอนโทรลเลอร์:

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

หากคุณพยายามตรวจสอบการเปลี่ยนแปลงคุณสมบัติมากมายอาจทำให้เกิดความสับสนได้ อาจไม่ควรทำเช่นนี้ในตัวควบคุม แต่ meh


9
สำหรับสิ่งที่คุ้มค่าคุณสามารถทำได้previous_changesโดยใช้ค่าเริ่มต้น
Jason Galuten

2
#update_attributesไม่ซ่อนการเปลี่ยนแปลง มันบันทึกบันทึกดังนั้นรูปแบบการปรับปรุงและไม่มีการเปลี่ยนแปลง คุณต้องเปลี่ยนฟิลด์ในโมเดลและตรวจสอบchanged?ก่อนที่จะบันทึก กล่าวคือ @ model.field = 'foo' หรือ@model.attributes = @model.attributes.merge(params[:model])
Grzegorz

9

สำหรับ rails 5.1+ callbacks

ตั้งแต่ Ruby on Rails 5.1 วิธีการattribute_changed?และattribute_wasActiveRecord จะถูกคัดค้าน

ใช้saved_change_to_attribute?แทนattribute_changed?

@user.saved_change_to_street1? # => true/false

ตัวอย่างเพิ่มเติมที่นี่


2

คำตอบข้างต้นดีกว่า แต่สำหรับความรู้เรามีการอนุมัติอื่นเช่นกันให้เปลี่ยนค่าคอลัมน์ 'หมวดหมู่' สำหรับวัตถุ (@design)

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

การเปลี่ยนแปลงจะส่งคืนแฮชที่มีคีย์เป็นชื่อคอลัมน์และค่าเป็นอาร์เรย์ที่มีสองค่า [old_value, new_value] สำหรับแต่ละคอลัมน์ ตัวอย่างประเภทด้านบนเปลี่ยนจาก 'ABC' เป็น 'XYZ' ของ @design

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

สำหรับการอ้างอิงการเปลี่ยนแปลงใน ROR

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.