Usando o Rails serialize para salvar hash no banco de dados

135

Estou tentando salvar um ID de mapeamento de hash em várias tentativas no meu aplicativo de trilhos. Minha migração para o banco de dados para acomodar esta nova coluna:

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

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

No meu modelo eu tenho:

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

Mas quando eu uso o console do Rails para testar isso, faça:

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

A saída é falsa. O que está acontecendo de errado aqui?

cmwright
fonte
4
Existe algo no user.errors depois de tentar salvar o registro?
Martijn
1
No futuro, você pode usar o método bang (salvar!) Para gerar uma exceção e exibir uma mensagem de erro.
Leishman
Melhor resposta agora usa uma coluna JSON stackoverflow.com/a/21397522/1536309
Blair Anderson

Respostas:

174

O tipo de coluna está incorreto. Você deve usar Texto em vez de String. Portanto, sua migração deve ser:

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

Em seguida, o Rails o converterá corretamente em YAML para você (e executará a serialização adequada). Os campos de strings são limitados em tamanho e conterão apenas valores especialmente pequenos.

Benjamin Tan Wei Hao
fonte
1
@BenjaminTan qual é a razão por trás disso, por que não consigo armazenar hash no tipo de dados 'string'.
Lohith MV 18/04
8
Porque no banco de dados, String tem um comprimento fixo de 255 (eu acho). Mas se você serializar um hash de tamanho comparativo, isso facilmente excederá o comprimento. O mesmo caso para matrizes. O texto permite comprimentos muito maiores.
Benjamin Tan Wei Hao
72

ATUALIZADA:

Implementação exata vai depender do seu banco de dados, mas o PostgreSQL tem agora jsone jsonbcolunas que pode nativamente armazenar seus dados de hash / objeto e permitir que você consulta contra o JSON com ActiveRecord !

altere sua migração e pronto.

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

ORIGINAL:

Para mais detalhes: rails docs && apidock

Verifique se a sua coluna está :texte não:string

Migração:

$ rails g migration add_location_data_to_users location_data:text

deve criar:

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

Sua classe seria semelhante a:

class User < ActiveRecord::Base
  serialize :location_data
end

Ações disponíveis:

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

Mais impressionante ?!

utilize o 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

Com o hstore, você pode definir atributos no campo serializado

class User < ActiveRecord::Base  
  # setup hstore
  store_accessor :location_data, :city, :state
end
Blair Anderson
fonte
2
Verdadeiramente incrível! Obrigado!
Alexander Gorg 12/06
18

O Rails 4 possui um novo recurso chamado Store , para que você possa usá-lo facilmente para resolver seu problema. Você pode definir um acessador para ele e é recomendável declarar a coluna do banco de dados usada para o armazenamento serializado como um texto, para que haja bastante espaço. O exemplo original:

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
fonte