How to use the ActiveRecord json field type
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.
Active Record will not complain if you forget to parse a JSON string:
game = Game.create(game_board: '"key":"value"')
When you retrieve a string from a json
attribute, it won't complain and just return the String.
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.
So make sure you use JSON.parse(string)
before saving.
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.
Yes, ActiveRecord allows to use Postgres' json
-fields simply as Hashes in their models. However, there are a couple of things to consider:
Hash may be NULL on initialization
In yourcreate_table
migration you allow the field:game_board
to beNULL
. Thus, on first usage the field:game_board
of your model-instance will beNULL
and you have to initialize the Hash first before using it. (See example below)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.
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:
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.
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