Ruby-on-rails 如何使用 ActiveRecord json 字段类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28102425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:03:29  来源:igfitidea点击:

How to use the ActiveRecord json field type

ruby-on-railsjsonpostgresqlruby-on-rails-4activerecord

提问by Freedom_Ben

I have a Rails model which has a database column of type "json":

我有一个 Rails 模型,它有一个“json”类型的数据库列:

create_table "games", force: true do |t|
  t.json     "game_board"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

Great! Now how do I use it? Is it really just as simple as treating the field like a Hash?

伟大的!现在我该如何使用它?真的像对待字段一样Hash简单吗?

self.game_board[:player1] = 1
self.game_board[:cards] = cards.to_hash

If I were to write that, would everything just work as expected, so in a future API call from a client I could do this?:

如果我这样写,一切都会按预期工作,所以在将来来自客户端的 API 调用中我可以这样做吗?:

self.game_board[:player] # And get back the 1 that I put here before

What about performance as well? Will the entire game_boardbe de-serialized every time even if that field is never read? Will the field be re-written (IOW a database write) each time I change part of the "Hash?"

性能呢?将整个game_board是反序列化,每次即使该场从来不读?每次更改“哈希”的一部分时,该字段是否会被重新写入(IOW 数据库写入)?

回答by Andreas Rayo Kniep

Yes, ActiveRecord allows to use Postgres' json-fields simply as Hashes in their models. However, there are a couple of things to consider:

是的,ActiveRecord 允许将 Postgres 的json-fields 简单地用作其模型中的哈希值。但是,有几件事情需要考虑:

  1. Hash may be NULL on initialization
    In your create_tablemigration you allow the field :game_boardto be NULL. Thus, on first usage the field :game_boardof your model-instance will be NULLand you have to initialize the Hash first before using it. (See example below)

  2. In JSON all keys are Strings
    Thus, on save (and reload) all keys will be transformed into Strings if you have used Symbols or Numbers before. Thus, to prevent unwanted behavior it is recommended to use String-keys unless your ORM is configured to symbolize all keys.

  1. 初始化时哈希可能为 NULL
    在您的create_table迁移中,您允许该字段:game_boardNULL. 因此,在第一次使用时:game_board,您的模型实例的字段将是NULL,您必须在使用它之前先初始化 Hash。(见下例)

  2. 在 JSON 中,所有键都是字符串。
    因此,如果您以前使用过符号或数字,则在保存(和重新加载)时,所有键都将转换为字符串。因此,为了防止不需要的行为,建议使用字符串键,除非您的 ORM 配置为符号化所有键。


Your examples:


你的例子:

self.game_board         ||= {}
self.game_board[:player1] = 1
self.game_board[:cards]   = cards.to_hash

# after reload from database (access via String-key):
self.game_board['player1']  # And retrieve value 1 (that we put here before)


@ Performance:


@ 表现:

  1. Yes, every time ActiveRecord reads an entry from the database and creates a model-instance, JSON-fields get unserialized into Hashes. But if you think that is a performance-hit to your application than you should either use a text-field and serialize/deserialize the JSON/Hashes when you need to or, even better, don't use ActiveRecord at all. By creating heaps of classes and using magic-methods, ActiveRecord creates so much overhead that you shouldn't worry about the deserialization of JSON. Convenience has its costs.

  2. Yes, every time you change a value in the Hash, the (whole) JSON-field gets replaced and updated with the new serialized version.
    Two notes on this:

    • Even in Postgres itself (not only in ActiveRecord) the possibility of performing updates on certain JSON-elements is missing until now. Compare this Stackoverflow-question
    • In general, JSON-fields should be used with a fixed structure or, at least, in manageable sizes and the field-type is not supposed to be a document-store like eg. in MongoDB. Compare the Postgres documentation
  1. 是的,每次 ActiveRecord 从数据库读取条目并创建模型实例时,JSON 字段都会被反序列化为哈希。但是,如果您认为这会影响您的应用程序的性能,那么您应该使用文本字段并在需要时序列化/反序列化 JSON/哈希,或者甚至更好,根本不使用 ActiveRecord。通过创建类堆并使用魔术方法,ActiveRecord 会产生如此多的开销,您不必担心 JSON 的反序列化。便利是有代价的。

  2. 是的,每次更改 Hash 中的值时,(整个)JSON 字段都会被替换并更新为新的序列化版本。
    对此有两点说明:

    • 即使在 Postgres 本身(不仅在 ActiveRecord 中),在某些 JSON 元素上执行更新的可能性直到现在也是缺失的。比较这个 Stackoverflow 问题
    • 一般来说,JSON 字段应该与固定结构一起使用,或者至少以可管理的大小使用,并且字段类型不应该是像例如这样的文档存储。在 MongoDB 中。比较 Postgres 文档

回答by Dylan Pierce

Just to further clarify - when you're saving the JSON object to an attribute of your model instance make sure to save it as a hash.

只是为了进一步澄清 - 当您将 JSON 对象保存到模型实例的属性时,请确保将其保存为 hash

Active Record will not complain if you forget to parse a JSON string:

如果您忘记解析 JSON字符串,Active Record 不会抱怨:

  game = Game.create(game_board: '"key":"value"')

When you retrieve a string from a jsonattribute, it won't complain and just return the String.

当您从json属性中检索字符串时,它不会抱怨并只返回字符串。

  game.game_board
  => '"key":"value"'

Thus game.game_board['key']would lead to an error because you're trying to treat a String like a Hash.

因此game.game_board['key']会导致错误,因为您试图将字符串视为哈希。

So make sure you use JSON.parse(string)before saving.

所以请确保JSON.parse(string)在保存前使用。

  game = Game.create(game_board: JSON.parse('"key":"value"'))

So now you have the expected behavior

所以现在你有了预期的行为

game.game_board['key']
=> 'value'

Probably not useful for this case, but came across this issue when saving a JSON payload from an API I was integrating with. Anyway, hope this helps.

在这种情况下可能没有用,但是在从我正在集成的 API 中保存 JSON 有效负载时遇到了这个问题。无论如何,希望这会有所帮助。