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

时间:2009-10-06 15:06:48

标签: python django django-models memoization

我的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)

3 个答案:

答案 0 :(得分:24)

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

Django使用它来缓存昂贵的操作,如url resolving。

答案 1 :(得分:16)

通常,我使用这样的模式:

def get_expensive_operation(self):
    if not hasattr(self, '_expensive_operation'):
        self._expensive_operation = self.expensive_operation()
    return self._expensive_operation

然后使用get_expensive_operation方法访问数据。

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

事实上有人已经这样做了:见here

答案 2 :(得分:1)

对于类方法,您应该使用django.utils.functional.cached_property

由于类方法的第一个参数是selfmemoize即使在你抛弃它之后也会保持对对象和函数结果的引用。这可能会导致垃圾收集器清理过时的对象,从而导致内存泄漏。 cached_property将丹尼尔的建议变成了装饰者。