L'uso di Rails serializza per salvare l'hash nel database


135

Sto provando a salvare un ID di mappatura dell'hash in una serie di tentativi nella mia app rails. La mia migrazione al database per accogliere questa nuova colonna:

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

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

Nel mio modello ho:

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

Ma quando uso la console di rotaie per testare questo facendo:

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

L'output è falso. Cosa non va qui?


4
C'è qualcosa in user.errors dopo aver tentato di salvare il record?
Martijn,

1
In futuro, puoi utilizzare il metodo bang (salva!) Per sollevare un'eccezione e visualizzare un messaggio di errore.
leishman

La migliore risposta ora utilizza una colonna JSON stackoverflow.com/a/21397522/1536309
Blair Anderson

Risposte:


174

Il tipo di colonna è errato. Dovresti usare Text invece di String. Pertanto, la migrazione dovrebbe essere:

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

Quindi Rails lo convertirà correttamente in YAML per te (ed eseguirà la serializzazione corretta). I campi delle stringhe hanno dimensioni limitate e conterranno solo valori particolarmente piccoli.


1
@BenjaminTan qual è il motivo dietro, perché non riesco a memorizzare l'hash nel tipo di dati 'stringa'.
Lohith MV,

8
Perché nel database, String ha una lunghezza fissa di 255 (credo). Ma se dovessi serializzare un hash di dimensioni comparative, questo supererebbe facilmente la lunghezza. Lo stesso caso per gli array. Il testo consente lunghezze molto maggiori.
Benjamin Tan Wei Hao,

72

AGGIORNAMENTO:

L'implementazione esatta dipenderà dal tuo database, ma PostgreSQL ha ora jsone jsonbcolonne che possono archiviare nativamente i tuoi dati hash / oggetto e che ti permettono di interrogare il JSON con ActiveRecord !

cambia la tua migrazione e il gioco è fatto.

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

ORIGINALE:

Per maggiori dettagli: rails docs && apidock

Assicurati che la tua colonna sia :texte non:string

Migrazione:

$ rails g migration add_location_data_to_users location_data:text

dovrebbe creare:

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

La tua classe sarebbe simile a:

class User < ActiveRecord::Base
  serialize :location_data
end

Azioni Disponibili:

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

Più fantastico ?!

utilizzare 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

Con hstore puoi impostare gli attributi sul campo serializzato

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

2
Davvero fantastico! Grazie!
Alexander Gorg,

18

Rails 4 ha una nuova funzionalità chiamata Store , quindi puoi usarla facilmente per risolvere il tuo problema. È possibile definire un accessor per esso ed è consigliabile dichiarare la colonna del database utilizzata per l'archivio serializzato come testo, quindi c'è molto spazio. L'esempio originale:

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'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.