16

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

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

我在哪里/如何存储备忘录?我对使用实例变量感到紧张,因为我不了解查询实例化任何特定 UserProfile 背后的魔力。使用安全,还是我__init__需要hasattr()在每次读取时检查 memo 属性的存在?

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

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)
4

3 回答 3

23

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

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

于 2010-04-14T03:28:16.770 回答
18

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

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.

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

于 2009-10-06T15:14:27.473 回答
2

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

由于类方法的第一个参数是self,memoize将保持对对象的引用和函数的结果,即使在您将其丢弃后也是如此。这会阻止垃圾收集器清理过时的对象,从而导致内存泄漏。cached_property将 Daniel 的建议变成了装饰器。

于 2017-10-14T00:05:42.113 回答