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:

  1. Hash may be NULL on initialization
    In your create_table migration you allow the field :game_board to be NULL. Thus, on first usage the field :game_board of your model-instance will be NULL and 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.


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