93 votes

comment stocker un objet complexe dans redis (en utilisant redis-py)

La fonction hmset peut définir la valeur de chaque champ, mais j'ai constaté que si la valeur elle-même est un objet structuré complexe, la valeur renvoyée par hget est une chaîne sérialisée, et non l'objet d'origine.

Par exemple

images= [{'type':'big', 'url':'....'},
     {'type':'big', 'url':'....'},
     {'type':'big', 'url':'....'}]   

redis = Redis()
redis.hset('photo:1', 'images', images)

i = redis.hget('photo:1', 'images')
print type(i)

le type de i est une chaîne de caractères, pas un objet python, y a-t-il un moyen de résoudre ce problème en dehors de l'analyse manuelle de chaque champ ?

155voto

En fait, vous pouvez stocker des objets python dans redis en utilisant le module intégré cornichon .

Voici un exemple.

import pickle
import redis

r = redis.StrictRedis(host='localhost', port=6379, db=0)
obj = ExampleObject()
pickled_object = pickle.dumps(obj)
r.set('some_key', pickled_object)
unpacked_object = pickle.loads(r.get('some_key'))
obj == unpacked_object

72voto

CivFan Points 38

Si vos données sont Sérialisable en JSON alors c'est peut-être la meilleure option que de sauvegarder les pickles python dans une base de données externe, puisque c'est un standard plus courant en dehors de Python, qu'il est plus lisible par l'homme, et qu'il évite les problèmes d'accès à la base de données. un vecteur d'attaque assez important .

Exemple JSON :

import json
import redis

r = redis.StrictRedis(host='localhost', port=6379, db=0)

images= [
    {'type':'big', 'url':'....'},
    {'type':'big', 'url':'....'},
    {'type':'big', 'url':'....'},
]

# Convert python dict to JSON str and save to Redis
json_images = json.dumps(images)
r.set('images', json_images)

# Read saved JSON str from Redis and unpack into python dict
unpacked_images = json.loads(r.get('images'))
images == unpacked_images

python 3 :

unpacked_images = json.loads(r.get('images').decode('utf-8'))
images == unpacked_images

62voto

Jonatan Hedborg Points 3205

Vous ne pouvez pas créer de structures imbriquées dans Redis, ce qui signifie que vous ne pouvez pas (par exemple) stocker une liste redis native à l'intérieur d'une carte de hachage redis native.

Si vous avez vraiment besoin de structures imbriquées, vous pouvez stocker un JSON-blob (ou quelque chose de similaire) à la place. Une autre option est de stocker un "id"/clé d'un objet redis différent comme valeur de la clé de la carte, mais cela nécessite plusieurs appels au serveur pour obtenir l'objet complet.

9voto

Doug T. Points 33360

J'ai créé une bibliothèque, SubRedis qui permet de créer des structures/hiérarchies beaucoup plus complexes dans redis. Si vous lui donnez une instance redis et un préfixe, vous obtiendrez une instance redis presque entièrement capable et indépendante.

redis = Redis()
photoRedis = SubRedis("photo:%s" % photoId, redis)
photoRedis.hmset('image0', images[0])
photoRedis.hmset('image1', images[1])
...

SubRedis se contente d'ajouter la chaîne de caractères qui lui a été transmise en tant que préfixe à la structure de données plate de Redis. Je trouve que c'est une enveloppe pratique pour un schéma que je fais souvent dans redis -- ajouter un identifiant pour imbriquer des données.

8voto

Curtis Yallop Points 639

Voici un simple wrapper autour de Redis qui permet de découper les structures de données :

from redis import Redis
from collections import MutableMapping
from pickle import loads, dumps

class RedisStore(MutableMapping):

    def __init__(self, engine):
        self._store = Redis.from_url(engine)

    def __getitem__(self, key):
        return loads(self._store[dumps(key)])

    def __setitem__(self, key, value):
        self._store[dumps(key)] = dumps(value)

    def __delitem__(self, key):
        del self._store[dumps(key)]

    def __iter__(self):
        return iter(self.keys())

    def __len__(self):
        return len(self._store.keys())

    def keys(self):
        return [loads(x) for x in self._store.keys()]

    def clear(self):
        self._store.flushdb()

d = RedisStore('redis://localhost:6379/0')
d['a'] = {'b': 1, 'c': 10}
print repr(d.items())
# this will not work: (it updates a temporary copy and not the real data)
d['a']['b'] = 2
print repr(d.items())
# this is how to update sub-structures:
t = d['a']
t['b'] = 2
d['a'] = t
print repr(d.items())
del d['a']

# Here is another way to implement dict-of-dict eg d['a']['b']
d[('a', 'b')] = 1
d[('a', 'b')] = 2
print repr(d.items())
# Hopefully you do not need the equivalent of d['a']
print repr([{x[0][1]: x[1]} for x in d.items() if x[0][0] == 'a'])
del d[('a', 'b')]
del d[('a', 'c')]

Si vous préférez des données lisibles en clair dans redis (pickle en stocke une version binaire), vous pouvez remplacer pickle.dumps par repr et pickle.loads par ast.literal_eval. Pour json, utilisez json.dumps et json.loads.

Si vous utilisez toujours des clés qui sont de simples chaînes de caractères, vous pouvez supprimer le décapage de la clé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X