Menggunakan Rails serialize untuk menyimpan hash ke database

135

Saya mencoba menyimpan id pemetaan hash ke sejumlah percobaan di aplikasi rel saya. Migrasi saya ke database untuk mengakomodasi kolom baru ini:

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

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

Dalam model saya, saya memiliki:

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

Tetapi ketika saya menggunakan konsol rel untuk menguji ini dengan melakukan:

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

Outputnya salah. Apa yang salah disini?

cmwright
sumber
4
Apakah ada sesuatu di user.errors setelah mencoba menyimpan record?
Martijn
1
Di masa mendatang, Anda dapat menggunakan metode bang (simpan!) Untuk memunculkan pengecualian dan menampilkan pesan kesalahan.
leishman
Jawaban terbaik sekarang menggunakan kolom JSON stackoverflow.com/a/21397522/1536309
Blair Anderson

Jawaban:

174

Jenis kolom salah. Anda harus menggunakan Teks, bukan String. Oleh karena itu, migrasi Anda harus:

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

Kemudian Rails akan mengubahnya dengan benar menjadi YAML untuk Anda (dan melakukan serialisasi yang benar). Bidang string berukuran terbatas dan hanya akan menampung nilai yang sangat kecil.

Benjamin Tan Wei Hao
sumber
1
@BenjaminTan apa alasan di baliknya, mengapa saya tidak bisa menyimpan hash dalam tipe data 'string'.
Lohith MV
8
Karena dalam database, String memiliki panjang tetap 255 (menurut saya). Tetapi jika Anda membuat serial hash dengan ukuran komparatif, ini dengan mudah akan melebihi panjangnya. Kasus yang sama untuk Array. Teks memungkinkan panjang yang jauh lebih besar.
Benjamin Tan Wei Hao
73

DIPERBARUI:

Implementasi yang tepat akan bergantung pada database Anda, tetapi PostgreSQL sekarang memiliki kolom jsondan jsonbyang secara native dapat menyimpan data hash / objek Anda dan memungkinkan Anda untuk melakukan kueri terhadap JSON dengan ActiveRecord !

ubah migrasi Anda dan Anda selesai.

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

ASLI:

Untuk detail selengkapnya: rails docs && apidock

Pastikan kolom Anda adalah :textdan tidak:string

Migrasi:

$ rails g migration add_location_data_to_users location_data:text

harus membuat:

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

Kelas Anda Akan Terlihat Seperti:

class User < ActiveRecord::Base
  serialize :location_data
end

Tindakan yang Tersedia:

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

Lebih Mengagumkan ?!

memanfaatkan hstore postgresql

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

Dengan hstore Anda dapat mengatur atribut pada bidang serial

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end
Blair Anderson
sumber
2
Benar-benar luar biasa! Terima kasih!
Alexander Gorg
18

Rails 4 memiliki fitur baru yang disebut Store , sehingga Anda dapat dengan mudah menggunakannya untuk menyelesaikan masalah Anda. Anda dapat menentukan pengakses untuk itu dan Anda disarankan untuk mendeklarasikan kolom database yang digunakan untuk penyimpanan berseri sebagai teks, jadi ada banyak ruang. Contoh asli:

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'
Aboozar Rajabi
sumber