เหตุใดแอตทริบิวต์โมเดล Rails จึงไม่สามารถเข้าถึงได้โดยใช้สัญลักษณ์แทนสตริง

ฉันจำเป็นต้องเปรียบเทียบค่าแอตทริบิวต์ของโมเดล Rails (2.3.11) บางส่วนก่อนและหลังการอัปเดตฐานข้อมูล ดังนั้นฉันจึงเริ่มต้นด้วยการค้นหาบันทึกของฉันและบันทึกค่าแอตทริบิวต์ที่มีอยู่ในแฮช ดังนี้:

id = params[:id]
work_effort = WorkEffort.find(id)

ancestor_rollup_fields = {
    :scheduled_completion_date => work_effort.scheduled_completion_date
}

work_effort.update_attributes(params.except(:controller, :action))
#etcetera

โปรดทราบว่าฉันปฏิบัติตาม "แนวทางปฏิบัติที่ดีที่สุด" ในการใช้สัญลักษณ์สำหรับแฮชคีย์

จากนั้นฉันมีวิธีการที่ใช้โมเดลและแฮชเพื่อกำหนดขั้นตอนเพิ่มเติมที่เป็นไปได้หากค่าจากแอตทริบิวต์แฮชและโมเดลไม่ตรงกัน เพื่อพิจารณาสิ่งนี้ ฉันพยายามรับค่าแอตทริบิวต์ model ในแต่ละลูป แต่ในตอนแรกฉันได้รับศูนย์:

def rollup_ancestor_updates(work_effort, ancestor_rollup_fields)
    ancestor_rollup_fields.each do |key, value|
        model_val = work_effort.attributes[key] #nil
        #etcetera

ในการดีบักข้างต้น ฉันสังเกตเห็นว่าฮาร์ดโค้ดสตริงเป็นคีย์:

work_effort.attribute['scheduled_completion_date']

กลับได้ค่าที่ต้องการ ดังนั้นในแต่ละบล็อกของฉัน ฉันลองทำสิ่งต่อไปนี้และได้ผล:

model_val = work_effort.attributes[key.to_s]

มีวิธีอื่นในการทำเช่นนี้หรือไม่? สำหรับฉันด้วยประสบการณ์ Ruby/Rails เพียง 3 เดือน การใช้สัญลักษณ์เป็นแฮชคีย์ตามแนวทางปฏิบัติที่ดีที่สุดที่กำหนดไว้นั้นทำให้เกิดความสับสน แต่จากนั้นต้องเรียก .to_s บนสัญลักษณ์เพื่อรับแอตทริบิวต์ model มีใครเคยประสบปัญหานี้ เคยแก้ไขปัญหานี้ เคยสับสนกับเรื่องนี้บ้างไหม ขอบคุณล่วงหน้า


person Dexygen    schedule 12.10.2011    source แหล่งที่มา


คำตอบ (4)


แฮชที่ส่งคืนเมื่อคุณโทร #attributes บนอินสแตนซ์ AR มีคีย์สตริง ซึ่งเป็นสาเหตุที่สัญลักษณ์เป็นดัชนีในแฮชไม่ทำงานในกรณีของคุณ มีคลาสย่อยของ Hash ที่เรียกว่า HashWithIndifferentAccess ซึ่งจะแปลงดัชนีสัญลักษณ์เป็นสตริงโดยอัตโนมัติ

บ่อยครั้งใน Rails คุณจะพบกับ HashWithIndifferentAccess อินสแตนซ์ ตัวอย่างที่สมบูรณ์แบบคือตัวแปร params ที่คุณเข้าถึงในคอนโทรลเลอร์และโค้ดมุมมอง

ลองใช้ work_effort.attributes.with_indifferent_access[key]

จริงๆ แล้วมันเป็นแค่การทำสิ่งเดียวกันกับที่คุณเป็น แต่มันทำอยู่เบื้องหลัง

person Wizard of Ogz    schedule 12.10.2011
comment
มันใช้งานได้ แต่มีเหตุผลใดบ้างที่ฉันไม่ควรใช้ key.to_s หรือเป็นข้อโต้แย้งที่น่าสนใจที่ทุกคนส่วนใหญ่ใช้ with_indifferent_access - person Dexygen; 12.10.2011
comment
ฉันจะใช้ key.to_s เว้นแต่ฉันจะมีเหตุผลที่น่าสนใจเช่นกัน ในกรณีของคุณปรากฏว่า key.to_s ราคาถูกกว่า with_indifferent_access - person Wizard of Ogz; 12.10.2011

คุณสามารถเขียนทับวิธีแอตทริบิวต์ได้ด้วยตัวเอง

เปิดชั้นเรียน WorkEffort ของคุณ

class WorkEffort
  def attributes
    super.symbolize_keys
  end
end

จากนั้นเมื่อคุณเรียก work_effort.attributes คุณจะมีคีย์สัญลักษณ์อยู่ในแฮช

person Yurui Zhang    schedule 19.02.2013

คุณอาจต้องการใช้: stringify_keys! ซึ่งใช้กันอย่างแพร่หลายทั่วทั้งโค้ด Rails

def rollup_ancestor_updates(work_effort, ancestor_rollup_fields)
  ancestor_rollup_fields.stringify_keys!.each do |key, value|
    model_val = work_effort.attributes[key]
  end
  #....
end
person charlysisto    schedule 12.10.2011
comment
สิ่งนี้จะถูกทิ้งลงในตัวอย่างโค้ดของฉันเพื่อให้มันทำงานโดยไม่ต้องใช้ key.to_s ได้อย่างไร ขอบคุณ; ในระหว่างนี้ ฉันกำลังตรวจสอบตัวอย่างอื่น (with_indifferent_access ซึ่งตอนนี้ฉันจำได้ว่าเคยเห็นในที่อื่น) - person Dexygen; 12.10.2011
comment
ฉันคิดว่าคุณควรจะพูดว่า symbolize_keys! แนวคิดนี้ดีกว่าการเรียก with_indifferent_access เพราะไม่ได้สร้างแฮชใหม่ เว้นแต่คุณจะต้องใช้ทั้งคีย์สตริงและสัญลักษณ์ - person Wizard of Ogz; 12.10.2011

โมเดล Rails ทั้งหมดของคุณสามารถมีชื่อแอตทริบิวต์ที่เป็นสัญลักษณ์ได้:

class ApplicationRecord < ActiveRecord::Base
  # ...

  class << self
    def attribute_names(symbols = false)
      return self.attribute_names.map(&:to_sym) if symbols

      super()
    end
  end
end

จากนั้นคุณสามารถโทร MyModel.attribute_names(symbols = true) และรับกลับ [:id, :my_attribute, :updated_at, :created_at, ...] ในขณะที่ยังคงอนุญาตให้ attribute_names ส่งกลับสตริง

โปรดทราบว่า คุณต้องมีวงเล็บหลัง super เพื่อหลีกเลี่ยงข้อผิดพลาดในการโต้แย้งด้วยฟังก์ชันที่กำหนดไว้ตั้งแต่แรก

person Sam    schedule 08.07.2019