如何使django中的@cached_property失效

54 投票
4 回答
23345 浏览
提问于 2025-04-18 05:31

我现在在一个模型类上使用 @cached_property,我想在保存的时候把它删除,这样下次调用的时候可以重新填充。请问我该怎么做呢?

class Amodel():
    #...model_fields....

    @cached_property
    def db_connection(self):
        #get some thing in the db and cache here


instance = Amodel.objects.get(id=1)
variable = instance.db_connection

Amodel.objects.select_for_update().filter(id=1).update(#some variable)
#invalidate instance.db_connection
#new_variable = instance.db_connection

谢谢

4 个回答

6

如果你不想用 tryexcept,而且想写更少的代码,可以使用:

if ("expensive_property" in obj.__dict__):
    del obj.expensive_property

或者:

if ("expensive_property" in obj.__dict__):
    delattr(obj, "expensive_property")

这样做会删除缓存的属性,下次访问时会重新计算这个属性。

更新:不要使用 if (hasattr(obj, "expensive_property")):!这样做会在属性还没被缓存的时候就计算它,并且总是返回 True

8

由于正在开发中,这里进行了大量编辑... 现在支持为给定的缓存属性使用多个标签。

我遇到了类似的问题,我有一组相关的 cached_property 对象,它们都需要同时失效。我是这样解决的:

  1. 扩展 cached_property 以接受标签值,并添加一个装饰器类方法:

    def __init__(self, func, *tags):
        self.func = func
        self.tags = frozenset(tags)
    
    @classmethod
    def tag(cls *tags):
        return lambda f: cls(f, *tags)
    
  2. 在我的其他对象中,使用我新的 cached_property.tag 装饰器类方法来定义带标签的 cached_property 方法:

    @cached_property.tag("foo_group")
    def foo(self):
        return "foo"
    
  3. 在使用新装饰器的对象上,写一个方法,通过遍历实例化对象的类的 __dict__ 来使所有带有指定标签的 cached_property 值失效。这可以防止意外调用所有的 cached_property 方法:

    def invalidate(self, tag):
        for key, value in self.__class__.__dict__.items():
            if isinstance(value, cached_property) and tag in value.tags:
                self.__dict__.pop(key, None)
    

现在,要使其失效,我只需调用 myobject.invalidate("foo_group")

19

我创建了一个Django模型的混合类,这个混合类的作用是当调用model.refresh_from_db()时,会使模型上所有的@cached_property属性失效。你也可以通过调用model.invalidate_cached_properties()手动使缓存的属性失效。

from django.utils.functional import cached_property


class InvalidateCachedPropertiesMixin():

    def refresh_from_db(self, *args, **kwargs):
        self.invalidate_cached_properties()
        return super().refresh_from_db(*args, **kwargs)
            
    def invalidate_cached_properties(self):
        for key, value in self.__class__.__dict__.items():
            if isinstance(value, cached_property):
                self.__dict__.pop(key, None)

https://gitlab.com/snippets/1747035

这个想法是受到Thomas Baden的回答启发的。

87

就像文档里说的那样,直接用 del 来删除它。这会导致下次访问时重新计算。

class SomeClass(object):
    
    @cached_property
    def expensive_property(self):
         return datetime.now()

obj = SomeClass()
print obj.expensive_property
print obj.expensive_property # outputs the same value as before
del obj.expensive_property
print obj.expensive_property # outputs new value

在Python 3中,使用 del 的方法是一样的。下面是一个 try/except 块的例子。

try:
    del obj.expensive_property 
except AttributeError:
    pass 

撰写回答