无法使Beaker缓存正常工作
我正在尝试使用Beaker的缓存库,但我就是搞不定它。
这是我的测试代码。
class IndexHandler():
@cache.cache('search_func', expire=300)
def get_results(self, query):
results = get_results(query)
return results
def get(self, query):
results = self.get_results(query)
return render_index(results=results)
我试过Beaker文档里的例子,但我看到的都是
<type 'exceptions.TypeError'> at /
can't pickle generator objects
显然我漏掉了什么,但我找不到解决办法。
顺便说一下,这个问题发生在缓存类型设置为“文件”的时候。
3 个回答
试试用 return list(results)
代替 return results
,看看这样是否有效。
这个 beaker 文件缓存需要能够把缓存的键和值都保存下来;但是大多数的迭代器和生成器是不能被保存的。
我注意到beaker
的文档没有明确提到这一点,但很明显,decorate函数必须把它调用时的参数进行序列化(也就是“打包”),这样才能用这些参数作为缓存的关键字,检查缓存中是否有这个条目,如果没有的话再添加进去。而生成器对象是不能被序列化的,这就是你看到的错误信息所说的。所以,这意味着query
很可能是一个生成器对象。
为了使用beaker或者其他类型的缓存,你应该传递的是可以被序列化的parameters
,而不是query
生成器对象。也就是说,你可以传递字符串、数字、字典、列表、元组等等,任何你觉得方便的组合方式,这样就能在get_results
函数内部“及时”构建出查询。这种方式下,参数是可以被序列化的,缓存也能正常工作。
如果方便的话,你可以创建一个简单的可序列化类,这个类的实例可以“代表”查询,模拟你需要的初始化和参数设置,并且只有在调用某个需要实际查询对象的方法时才进行即时创建。但这只是一个“方便”的想法,并不会改变前面提到的基本概念。
如果你把beaker配置成保存到文件系统,你会发现每个参数也会被序列化(也就是变成一种可以存储的格式)。举个例子:
tp3
sS'tags <myapp.controllers.tags.TagsController object at 0x103363c10> <MySQLdb.cursors.Cursor object at 0x103363dd0> apple'
p4
注意缓存的“键”不仅仅包含我的关键词“apple”,还包含了一些特定于实例的信息。这其实很糟糕,因为特别是‘self’在每次调用时都不会相同。这样缓存每次都会失效(而且会被一些没用的键填满)。
带有缓存注解的方法只应该包含与你想要的“键”相对应的参数。换句话说,假设你想存储“John”对应的值555-1212,并且想要缓存这个信息。你的函数只应该接受一个字符串作为参数。你传入的任何参数在每次调用时都应该保持不变,所以像“self”这样的参数就不合适。
让这个工作变得简单的一种方法是把函数内联,这样你就不需要传递除了键以外的其他东西。例如:
def index(self):
# some code here
# suppose 'place' is a string that you're using as a key. maybe
# you're caching a description for cities and 'place' would be "New York"
# in one instance
@cache_region('long_term', 'place_desc')
def getDescriptionForPlace(place):
# perform expensive operation here
description = ...
return description
# this will either fetch the data or just load it from the cache
description = getDescriptionForPlace(place)
你的缓存文件应该看起来像下面这样。注意只有'place_desc'和'John'被保存为键。
tp3
sS'place_desc John'
p4