我的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)
答案 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_python
和get_db_prep_save
。
事实上有人已经这样做了:见here。
答案 2 :(得分:1)
对于类方法,您应该使用django.utils.functional.cached_property
。
由于类方法的第一个参数是self
,memoize
即使在你抛弃它之后也会保持对对象和函数结果的引用。这可能会导致垃圾收集器清理过时的对象,从而导致内存泄漏。 cached_property
将丹尼尔的建议变成了装饰者。