在以下示例中,cached_attr
用于在调用数据库昂贵的属性(示例中为related_spam
)时获取或设置模型实例上的属性。在示例中,我使用cached_spam
来保存查询。我在设置和获取值时放置了print语句,以便我可以测试它。我在一个视图中测试了它,方法是使用Egg
将{{ egg.cached_spam }}
实例传递到视图和视图中,以及Egg
模型上调用cached_spam
的其他方法} 他们自己。当我完成并测试它时,Django的开发服务器中的shell输出显示属性缓存被错过了好几次,并成功获得了几次。这似乎是不一致的。使用相同的数据,当我进行小的更改(只需更改print语句的字符串)并刷新(使用所有相同的数据)时,会发生不同数量的未命中/成功。这是怎么发生的?这段代码不正确或有问题吗?
class Egg(models.Model):
... fields
@property
def related_spam(self):
# Each time this property is called the database is queried (expected).
return Spam.objects.filter(egg=self).all() # Spam has foreign key to Egg.
@property
def cached_spam(self):
# This should call self.related_spam the first time, and then return
# cached results every time after that.
return self.cached_attr('related_spam')
def cached_attr(self, attr):
"""This method (normally attached via an abstract base class, but put
directly on the model for this example) attempts to return a cached
version of a requested attribute, and calls the actual attribute when
the cached version isn't available."""
try:
value = getattr(self, '_p_cache_{0}'.format(attr))
print('GETTING - {0}'.format(value))
except AttributeError:
value = getattr(self, attr)
print('SETTING - {0}'.format(value))
setattr(self, '_p_cache_{0}'.format(attr), value)
return value
答案 0 :(得分:9)
就你的代码而言,没有任何问题。问题可能不在那里,而在于你如何使用该代码。
要实现的主要事情是模型实例没有标识。这意味着如果你在某个地方实例化一个Egg对象,而在其他地方实例化另一个,即使它们引用相同的底层数据库行,它们也不会共享内部状态。因此,在一个上调用cached_attr
不会导致在另一个中填充缓存。
例如,假设您有一个带有ForeignKey to Egg的RelatedObject类:
my_first_egg = Egg.objects.get(pk=1)
my_related_object = RelatedObject.objects.get(egg__pk=1)
my_second_egg = my_related_object.egg
这里my_first_egg
和my_second_egg
都引用了pk 1的数据库行,但它们不是同一个对象:
>>> my_first_egg.pk == my_second_egg.pk
True
>>> my_first_egg is my_second_egg
False
因此,填写my_first_egg
上的缓存并未将其填充到my_second_egg
。
当然,对象不会在请求中持续存在(除非它们是专门设置为全局的,这很可怕),因此缓存也不会持久存在。
答案 1 :(得分:1)
扩展的Http服务器是无共享的;你不能依赖任何单身人士。要共享状态,您需要连接到特殊用途的服务。
Django的caching support适合您的用例。它也不一定是全球单身人士;如果你使用locmem://
,它将是进程本地的,这可能是更有效的选择。