2013-03-05 189 views
37

hmset函数可以设置每个字段的值,但我发现如果值本身是一个复杂的结构化对象,则从hget返回的值是一个序列化字符串,而不是原始对象如何在redis中存储复杂对象(使用redis-py)

例如

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) 

i的类型是一个字符串,而不是一个对象蟒,是否有任何方式来手动除了解决这个问题解析每个字段?

回答

40

您不能在Redis中创建嵌套结构,这意味着您无法(例如)在原生redis哈希映射中存储本机重做列表。

如果你确实需要嵌套结构,你可能只想存储一个JSON-blob(或类似的东西)。另一种选择是将“id”/ key存储到不同的redis对象中作为map key的值,但这需要多次调用服务器才能获得完整的对象。

+0

哦,还有一件事;使用EVAL(服务器端ruby脚本)可能会产生一个奇怪的复合查询:http://redis.io/commands/eval – 2013-03-05 09:54:05

-2

你可以只存储您的结构是,做一个“EVAL”从字符串转换为对象:

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 
+11

这是不必要的危险:任意Python代码通过'eval()'来评估。 JSON方法不会因此而受到影响。 – EOL 2014-03-15 15:53:51

58

其实,你可以使用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 
+17

这很危险:unpickling可以执行代码。 JSON解决方案更强大。 – EOL 2014-03-15 15:55:11

+0

@EOL你可以在这方面做进一步的工作吗?为什么会执行代码是一件坏事? – ealeon 2014-07-10 17:14:51

+1

我的意思是酸洗允许执行* untrusted *代码,因为有问题的代码可能是恶意的(它可以擦除文件等),所以最好避免这种代码。 – EOL 2014-07-11 12:25:09

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中做了很多事情 - 预先添加一些id来嵌套一些数据。

29

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 

蟒蛇3:

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

从'redis.get()'获取btyes对象是否正常? python 3 – 2016-01-20 16:08:27

+3

@shangyeshen看起来像'json.loads()'和python3中的'bytes'对象不太合适。您需要将'redis.get()'的'bytes'结果解码为python3'str'。看到新的编辑。 – CivFan 2016-01-20 17:10:43

2

可以使用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 

它转换蟒类型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

1

这里是围绕Redis的这酱菜/ unpickles数据结构一个简单的包装:

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的明文可读的数据(泡菜店它的二进制版本),你可以用repr和pickle.loads替换pickle.dumps和ast.literal_eval。对于json,请使用json.dumps和json.loads。

如果您始终使用简单字符串的键,则可以从键中移除酸洗。