如何在django中使@cached_property无效

use*_*168 23 django django-models django-views

大家好我现在正在模型类上使用@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
Run Code Online (Sandbox Code Playgroud)

谢谢

iso*_*lev 41

正如文档所说,只是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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果尚未访问/缓存该属性,则会生成AttributeError.在那种情况下,使用try包装它,除了AttributeError. (15认同)
  • 好消息@dalore; django开发者的可怕样板:( (3认同)
  • `@cached_property` 绝对*不是*有点糟糕的样板!这是实例字典和描述符协议之间相互作用的简明使用。如果你在属性被调用/缓存之前尝试使用 `delattr` 那么它会抛出 `AttributeError`,这是沼泽标准的 Python 行为,这是完全有道理的。这是完全合理的,因为`cached_property` 精确地*写入* 实例字典的属性,而不是在第一次运行后调用自身。花点时间研究它,您会发现它是一个既实用又经济的代码块。 (3认同)
  • 请注意,如果你从类中失效,你将使用`del self .__ dict __ ['expensive_property']`...除非有一种我不知道的非dunder方式. (2认同)

git*_*rik 6

我创建了一个Django模型mixin,@cached_property该模型在model.refresh_from_db()调用时会使模型上的所有属性无效。您也可以使用来使缓存的属性无效model.invalidate_cached_properties()

from django.utils.functional import cached_property


class RefreshFromDbInvalidatesCachedPropertiesMixin():

    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)
Run Code Online (Sandbox Code Playgroud)

https://gitlab.com/snippets/1747035

受到Thomas Baden的回答的启发。

  • 如果您使用“from functools import cached_property”,它将不起作用。所以我建议,检查多个字段类型:`if isinstance(value, (cached_property, django_cached_property)):` (3认同)

Tho*_*den 5

由于正在进行中的开发,因此进行了大量编辑...现在支持给定cached_property的多个标签。

我遇到了类似的问题,其中我有一组相关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)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在其他对象中,使用新的cached_property.tag装饰器类cached_property方法定义标记的方法:

    @cached_property.tag("foo_group")
    def foo(self):
        return "foo"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在使用新装饰器的对象上,编写一种方法,cached_property通过遍历__dict__实例化对象的类的来使带有命名标签的所有值无效。这样可以防止意外调用所有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)
    
    Run Code Online (Sandbox Code Playgroud)

现在,为了使无效,我仅调用myobject.invalidate("foo_group")


Uri*_*Uri 5

如果你不想使用tryand except,也不想写更少的行,你可以使用:

if (hasattr(obj, "expensive_property")):
    delattr(obj, "expensive_property")
Run Code Online (Sandbox Code Playgroud)

或者:

if (hasattr(obj, "expensive_property")):
    del obj.expensive_property
Run Code Online (Sandbox Code Playgroud)

它将删除缓存的属性,并在下次访问时再次计算。