如何在Django模型对象上记忆昂贵的计算?

Dav*_*Eyk 14 python django memoization django-models

我的UserProfile对象上有几个包含JSON对象的TextField列.我还为每个列定义了一个setter/getter属性,它封装了将JSON序列化和反序列化为python数据结构的逻辑.

此数据的性质可确保在单个请求中通过视图和模板逻辑多次访问它.为了节省反序列化成本,我想在读取时记住python数据结构,在直接写入属性时无效或从模型对象保存信号.

在哪里/如何存储备忘录?我对使用实例变量感到紧张,因为我不理解查询实例化任何特定UserProfile背后的魔力.是__init__使用安全,或是否需要通过检查备忘录属性的存在hasattr(),在每个读?

这是我当前实现的一个示例:

class UserProfile(Model):
    text_json = models.TextField(default=text_defaults)

    @property
    def text(self):
        if not hasattr(self, "text_memo"):
            self.text_memo = None
        self.text_memo = self.text_memo or simplejson.loads(self.text_json)
        return self.text_memo
    @text.setter
    def text(self, value=None):
        self.text_memo = None
        self.text_json = simplejson.dumps(value)
Run Code Online (Sandbox Code Playgroud)

sat*_*oru 24

您可能对内置的django装饰器感兴趣django.utils.functional.memoize.

Django使用它来缓存昂贵的操作,如url解析.

  • 对于类方法,我更喜欢使用`django.utils.functional.cached_property`.由于类方法的第一个参数是`self`,memoize将保持对该对象的引用以及该函数的结果,即使在抛出它之后也是如此.这可能会导致垃圾收集器清理过时的对象,从而导致内存泄漏.`cached_property`将丹尼尔的建议变成了装饰者. (17认同)
  • @iTayb - 你可能应该详细说明...... [`memoize`已被弃用](https://docs.djangoproject.com/en/1.9/releases/1.7/#django-utils-functional-memoize-function)赞成`django.utils.lru_cache.lru_cache`或`functools.lru_cache`.但是,`cached_property`仍然可以在1.9中使用,并且不被弃用. (4认同)
  • 如果有人有兴趣,[cached_property](https://docs.djangoproject.com/en/1.6/ref/utils/#django.utils.functional.cached_property)是自Django 1.5以来的文档 (2认同)
  • 自1.9以来已弃用. (2认同)

Dan*_*man 18

一般来说,我使用这样的模式:

def get_expensive_operation(self):
    if not hasattr(self, '_expensive_operation'):
        self._expensive_operation = self.expensive_operation()
    return self._expensive_operation
Run Code Online (Sandbox Code Playgroud)

然后使用该get_expensive_operation方法访问数据.

但是,在您的特定情况下,我认为您正在以一种错误的方式接近这一点.首次从数据库加载模型时需要进行反序列化,并且仅在保存时进行序列化.然后,您每次只需将属性作为标准Python字典进行访问即可.您可以通过定义自定义JSONField类型,子类化models.TextField来覆盖to_python和执行此操作get_db_prep_save.

事实上有人已经做到了:看到这里.