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

139

我试图在我的Rails应用中保存一个将ID映射到尝试次数的哈希表。 我的数据迁移以适应这个新列:

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

输出为false。这里出了什么问题?


4
尝试保存记录后,user.errors 中有任何内容吗? - Martijn
1
将来,您可以使用感叹号方法(save!)来引发异常并显示错误消息。 - leishman
最佳答案现在使用JSON列 https://dev59.com/K2w15IYBdhLWcg3wT6D2#21397522 - Blair Anderson
3个回答

175

列类型错误,您应该使用Text而不是String。因此,您的迁移应该如下所示:

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

然后Rails将会为您正确地将其转换为YAML(并执行正确的序列化)。字符串字段大小有限,并且只能容纳特别小的值。


1
@BenjaminTan,这背后的原因是什么?为什么我不能将哈希值存储在“字符串”数据类型中? - Lohith MV
8
因为在数据库中,String类型有一个固定的长度限制为255个字符(我认为是这样)。但如果你要序列化一个同等大小的哈希值,它很容易超过这个长度。数组也是同样的情况。文本类型允许更长的长度。 - Benjamin Tan Wei Hao

77

更新:

确切的实现方式取决于您的数据库,但是PostgreSQL现在有jsonjsonb列,可以本地存储哈希/对象数据,并允许您通过ActiveRecord 查询JSON

更改您的迁移,完成。

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

原文:

更多细节请参考:rails文档 && 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
真的很棒!谢谢! - Alexander Gorg

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'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接