Verwenden von Rails serialisieren, um Hash in der Datenbank zu speichern

135

Ich versuche, Hash-Mapping-IDs für eine Reihe von Versuchen in meiner Rails-App zu speichern. Meine Migration in die Datenbank, um diese neue Spalte aufzunehmen:

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

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

In meinem Modell habe ich:

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

Aber wenn ich die Rails-Konsole benutze, um dies zu testen, indem ich Folgendes mache:

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

Die Ausgabe ist falsch. Was läuft hier falsch?

cmwright
quelle
4
Gibt es irgendetwas in user.errors, nachdem versucht wurde, den Datensatz zu speichern?
Martijn
1
In Zukunft können Sie mit der Bang-Methode (Speichern!) Eine Ausnahme auslösen und eine Fehlermeldung anzeigen.
Leishman
Die beste Antwort verwendet jetzt eine JSON-Spalte stackoverflow.com/a/21397522/1536309
Blair Anderson

Antworten:

174

Der Spaltentyp ist falsch. Sie sollten Text anstelle von String verwenden. Daher sollte Ihre Migration wie folgt sein:

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

Dann konvertiert Rails es für Sie ordnungsgemäß in YAML (und führt die ordnungsgemäße Serialisierung durch). Zeichenfolgenfelder sind in ihrer Größe begrenzt und enthalten nur besonders kleine Werte.

Benjamin Tan Wei Hao
quelle
1
@BenjaminTan Was ist der Grund dafür, warum kann ich keinen Hash im Datentyp 'string' speichern?
Lohith MV
8
Weil String in der Datenbank eine feste Länge von 255 hat (glaube ich). Wenn Sie jedoch einen Hash mit vergleichbarer Größe serialisieren würden, würde dies die Länge leicht überschreiten. Gleicher Fall für Arrays. Text ermöglicht viel größere Längen.
Benjamin Tan Wei Hao
72

AKTUALISIERT:

Genaue Umsetzung auf Ihrer Datenbank ab, sondern PostgreSQL hat jetzt jsonund jsonbSpalten , die Ihre Hash - nativ speichern kann / Objektdaten und ermöglicht es Ihnen , zu Abfrage für die JSON mit Active !

Ändern Sie Ihre Migration und Sie sind fertig.

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

ORIGINAL:

Für weitere Details: Rails Docs && Apidock

Stellen Sie sicher, dass Ihre Spalte ist :textund nicht:string

Migration:

$ rails g migration add_location_data_to_users location_data:text

sollte erstellen:

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

Ihre Klasse würde aussehen wie:

class User < ActiveRecord::Base
  serialize :location_data
end

Mögliche Aktionen:

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

Mehr genial?!

Verwenden Sie den Postgresql-Shop

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

Mit hstore können Sie Attribute für das serialisierte Feld festlegen

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end
Blair Anderson
quelle
2
Wirklich großartig! Vielen Dank!
Alexander Gorg
18

Rails 4 verfügt über eine neue Funktion namens Store , mit der Sie Ihr Problem problemlos lösen können. Sie können einen Accessor dafür definieren. Es wird empfohlen, die für den serialisierten Speicher verwendete Datenbankspalte als Text zu deklarieren, damit genügend Platz vorhanden ist. Das ursprüngliche Beispiel:

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
quelle