ใช้ Rails ทำให้เป็นอนุกรมเพื่อบันทึกแฮชลงในฐานข้อมูล


135

ฉันพยายามบันทึกรหัสการแมปแฮชเป็นจำนวนครั้งในแอปทางรถไฟของฉัน การย้ายฐานข้อมูลของฉันเพื่อรองรับคอลัมน์ใหม่นี้:

class AddMultiWrongToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :multi_wrong, :string
  end

  def self.down
    remove_column :users, :multi_wrong
  end
end

ในรุ่นของฉันฉันมี:

class User < ActiveRecord::Base 
 serialize :multi_wrong, Hash
end

แต่เมื่อฉันใช้คอนโซลรางเพื่อทดสอบสิ่งนี้โดยทำ:

user = User.create()
user.multi_wrong = {"test"=>"123"}
user.save

ผลลัพธ์เป็นเท็จ เกิดอะไรขึ้นที่นี่?


4
มีอะไรใน user.errors หลังจากพยายามบันทึกเรกคอร์ดหรือไม่
Martijn

1
ในอนาคตคุณสามารถใช้วิธีปัง (บันทึก!) เพื่อเพิ่มข้อยกเว้นและแสดงข้อความแสดงข้อผิดพลาด
leishman

คำตอบที่ดีที่สุดตอนนี้ใช้คอลัมน์ JSON stackoverflow.com/a/21397522/1536309
Blair Anderson

คำตอบ:


174

ประเภทคอลัมน์ไม่ถูกต้อง คุณควรใช้ Text แทน String ดังนั้นการย้ายข้อมูลของคุณควรเป็น:

 def self.up
   add_column :users, :multi_wrong, :text
 end

จากนั้น Rails จะแปลงเป็น YAML ให้คุณอย่างถูกต้อง (และทำการอนุกรมที่เหมาะสม) ช่องสตริงมีขนาด จำกัด และจะเก็บเฉพาะค่าที่มีขนาดเล็กเป็นพิเศษเท่านั้น


1
@BenjaminTan เหตุผลเบื้องหลังคืออะไรทำไมฉันไม่สามารถจัดเก็บแฮชในประเภทข้อมูล 'สตริง' ได้
Lohith MV

8
เนื่องจากในฐานข้อมูล String มีความยาวคงที่ 255 (ฉันคิดว่า) แต่ถ้าคุณจะทำให้เป็นอนุกรมของแฮชที่มีขนาดเปรียบเทียบสิ่งนี้จะเกินความยาวได้อย่างง่ายดาย กรณีเดียวกันสำหรับอาร์เรย์ ข้อความอนุญาตให้มีความยาวมากขึ้น
Benjamin Tan Wei Hao

73

ปรับปรุง:

การใช้งานที่แน่นอนจะขึ้นอยู่กับฐานข้อมูลของคุณ แต่ตอนนี้ PostgreSQL มีjsonและjsonbคอลัมน์ที่สามารถจัดเก็บข้อมูลแฮช / ออบเจ็กต์ของคุณได้และอนุญาตให้คุณสอบถามกับ JSON ด้วย ActiveRecord !

เปลี่ยนการย้ายข้อมูลของคุณและเสร็จสิ้น

class Migration0001
  def change
    add_column :users, :location_data, :json, default: {}
  end
end

เดิม:

สำหรับรายละเอียดเพิ่มเติม: รางเอกสาร && apidock

ตรวจสอบให้แน่ใจว่าคอลัมน์ของคุณ:textไม่ใช่:string

การโยกย้าย:

$ rails g migration add_location_data_to_users location_data:text

ควรสร้าง:

class Migration0001
  def change
    add_column :users, :location_data, :text
  end
end

ชั้นเรียนของคุณจะดูเหมือน:

class User < ActiveRecord::Base
  serialize :location_data
end

การดำเนินการที่มีอยู่:

b = User.new
b.location_data = [1,2,{foot: 3, bart: "noodles"}]
b.save

เจ๋งกว่า?!

ใช้ postgresql hstore

class AddHstore < ActiveRecord::Migration  
  def up
    enable_extension :hstore
  end

  def down
    disable_extension :hstore
  end
end 

class Migration0001
  def change
    add_column :users, :location_data, :hstore
  end
end

ด้วย hstore คุณสามารถตั้งค่าแอตทริบิวต์ในฟิลด์ซีเรียลไลซ์

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end

2
สุดยอดจริงๆ! ขอบคุณ!
Alexander Gorg

18

Rails 4 มีคุณสมบัติใหม่ที่เรียกว่าStoreเพื่อให้คุณสามารถใช้มันเพื่อแก้ปัญหาของคุณได้อย่างง่ายดาย คุณสามารถกำหนดตัวเข้าถึงได้และขอแนะนำให้คุณประกาศคอลัมน์ฐานข้อมูลที่ใช้สำหรับการจัดเก็บแบบอนุกรมเป็นข้อความดังนั้นจึงมีที่ว่างมากมาย ตัวอย่างเดิม:

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ], coder: JSON
end

u = User.new(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor

# There is no difference between strings and symbols for accessing custom attributes
u.settings[:country]  # => 'Denmark'
u.settings['country'] # => 'Denmark'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.