Подтвердить что ты не робот

Как хранить сложный объект в redis (используя redis-py)

Функция hmset может устанавливать значение каждого поля, но я обнаружил, что если само значение представляет собой сложный структурированный объект, значение return from hget является сериализованной строкой, а не исходным объектом

например

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)

тип я - это строка, а не объект python, есть ли способ решить эту проблему, помимо ручного анализа каждого поля?

4b9b3361

Ответ 1

Вы не можете создавать вложенные структуры в Redis, то есть вы не можете (например) сохранить собственный список redis внутри собственной хэш-карты redis.

Если вам действительно нужны вложенные структуры, вы можете просто сохранить JSON-blob (или что-то подобное). Другим вариантом является сохранение "id" /key для другого объекта redis в качестве значения ключа карты, но для получения полного объекта требуется несколько вызовов на сервер.

Ответ 2

Собственно, вы можете хранить объекты python в redis с помощью встроенного модуля pickle.

Вот пример.

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

Ответ 3

Пример JSON:

import json
import redis

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

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

json_images = json.dumps(images)
r.set('images', json_images)
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

Ответ 4

Я создал библиотеку SubRedis, которая позволяет создавать гораздо более сложные структуры/иерархии в Redis. Если вы дадите ему экземпляр Redis и префикс, он даст вам почти полностью работоспособный и независимый экземпляр Redis.

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

SubRedis просто заканчивает тем, что добавляет строку, переданную в него в качестве префикса, к плоской структуре данных redis. Я считаю, что это удобная оболочка для шаблона, который я в конечном итоге много делаю в Redis - добавляя некоторый идентификатор для вложения некоторых данных.

Ответ 5

Вы можете использовать библиотеку RedisWorks.

pip install redisworks

>>> from redisworks import Root
>>> root = Root()
>>> root.something = {1:"a", "b": {2: 2}}  # saves it as Hash
>>> print(root.something)  # loads it from Redis
{'b': {2: 2}, 1: 'a'}
>>> root.something['b'][2]
2

Он преобразует типы python в типы Redis и наоборот.

>>> root.sides = [10, [1, 2]]  # saves it as list in Redis.
>>> print(root.sides)  # loads it from Redis
[10, [1, 2]]
>>> type(root.sides[1])
<class 'list'>

Отказ от ответственности: я написал библиотеку. Вот код: https://github.com/seperman/redisworks

Ответ 6

Вот простая обёртка вокруг Redis, которая выбирает/раскручивает структуры данных:

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')]

Если вы предпочитаете данные, читаемые в виде открытого текста, в Redis (Pickle хранит двоичную версию), вы можете заменить Pickle.dumps на repr, а pickle.loads на ast.literal_eval. Для json используйте json.dumps и json.loads.

Если вы всегда используете ключи, которые являются простой строкой, вы можете удалить травление из ключа.

Ответ 7

Вы можете просто сохранить свою структуру как есть и сделать "eval" для преобразования из String в Object:

images= [{'type':'big', 'url':'....'},
 {'type':'big', 'url':'....'},
 {'type':'big', 'url':'....'}]   
redis = Redis()
redis.hset('photo:1', 'images', images)

i = eval(redis.hget('photo:1', 'images'))
print type(i) #type of i should be list instead of string now