使用Rails序列化将哈希保存到数据库


135

我尝试将哈希映射ID保存到Rails应用程序中的许多尝试中。我迁移到数据库以容纳此新列:

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

但是,当我使用Rails控制台通过以下操作进行测试时:

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

输出为假。这是怎么了


4
尝试保存记录后,user.errors中是否有任何内容?
Martijn

1
将来,您可以使用bang方法(保存!)引发异常并显示错误消息。
利什曼2015年

最佳答案现在使用JSON列stackoverflow.com/a/21397522/1536309
Blair Anderson

Answers:


174

列类型错误。您应该使用文本而不是字符串。因此,您的迁移应为:

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

然后,Rails将为您正确地将其转换为YAML(并执行适当的序列化)。字符串字段的大小受到限制,并且仅包含特别小的值。


1
@BenjaminTan它背后的原因是什么,为什么我不能以'string'数据类型存储哈希。
Lohith MV

8
因为在数据库中,String具有255的固定长度(我认为)。但是,如果要序列化比较大小的哈希,则很容易超过长度。数组的情况相同。文本允许更大的长度。
Benjamin Tan Wei Hao

72

更新:

确切的实现将取决于您的数据库,但是PostgreSQL现在具有jsonjsonb列,它们可以本地存储您的哈希/对象数据,并允许您使用ActiveRecord对JSON查询

更改您的迁移就可以了。

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

原版的:

有关更多详细信息:rails docs && 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
真棒!谢谢!
亚历山大·高尔

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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.