J'ai un modèle Rails qui a une colonne de base de données de type "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
Génial! Maintenant, comment dois-je l'utiliser? Est-ce vraiment aussi simple que traiter le champ comme un Hash
?
self.game_board[:player1] = 1
self.game_board[:cards] = cards.to_hash
Si je devais écrire cela, tout fonctionnerait-il comme prévu, donc dans un futur appel API d'un client, je pourrais le faire?:
self.game_board[:player] # And get back the 1 that I put here before
Et la performance aussi? Est-ce que l'ensemble game_board
être désérialisé à chaque fois même si ce champ n'est jamais lu? Le champ sera-t-il réécrit (IOW une écriture de base de données) à chaque fois que je change une partie du "Hash?"
Oui, ActiveRecord permet d'utiliser les champs json
- de Postgres simplement comme des hachages dans leurs modèles. Cependant, il y a quelques points à considérer:
Le hachage peut être NULL à l'initialisation
Dans votre create_table
migration vous autorisez le champ :game_board
être NULL
. Ainsi, lors de la première utilisation, le champ :game_board
de votre instance de modèle sera NULL
et vous devez d'abord initialiser le hachage avant de l'utiliser. (Voir l'exemple ci-dessous)
En JSON, toutes les clés sont des chaînes
Ainsi, lors de l'enregistrement (et du rechargement), toutes les clés seront transformées en chaînes si vous avez déjà utilisé des symboles ou des nombres. Ainsi, pour éviter tout comportement indésirable, il est recommandé d'utiliser des clés de chaîne, sauf si votre ORM est configuré pour symboliser toutes les clés.
Vos exemples:
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:
Oui, chaque fois qu'ActiveRecord lit une entrée de la base de données et crée une instance de modèle, les champs JSON ne sont pas sérialisés dans les hachages. Mais si vous pensez que cela nuit aux performances de votre application, vous devez soit utiliser un champ de texte et sérialiser/désérialiser le JSON/Hashs lorsque vous en avez besoin ou, mieux encore, ne pas utiliser ActiveRecord du tout. En créant des tas de classes et en utilisant des méthodes magiques, ActiveRecord crée tellement de surcharge que vous ne devriez pas vous soucier de la désérialisation de JSON. La commodité a ses coûts.
Oui, chaque fois que vous modifiez une valeur dans le hachage, le champ JSON (entier) est remplacé et mis à jour avec la nouvelle version sérialisée.
Deux notes à ce sujet:
Juste pour clarifier davantage - lorsque vous enregistrez l'objet JSON dans un attribut de votre instance de modèle assurez-vous de l'enregistrer en tant que hachage.
Active Record ne se plaindra pas si vous oubliez d'analyser une chaîne JSON :
game = Game.create(game_board: '"key":"value"')
Lorsque vous récupérez une chaîne à partir d'un attribut json
, il ne se plaindra pas et renverra simplement la chaîne.
game.game_board
=> '"key":"value"'
Ainsi, game.game_board['key']
Entraînerait une erreur car vous essayez de traiter une chaîne comme un hachage.
Assurez-vous donc d'utiliser JSON.parse(string)
avant d'enregistrer.
game = Game.create(game_board: JSON.parse('"key":"value"'))
Alors maintenant, vous avez le comportement attendu
game.game_board['key']
=> 'value'
Probablement pas utile dans ce cas, mais j'ai rencontré ce problème lors de l'enregistrement d'une charge utile JSON à partir d'une API avec laquelle j'étais en cours d'intégration. Quoi qu'il en soit, j'espère que cela vous aidera.