¿Cómo memorizo ​​los cálculos costosos en los objetos del modelo Django?

Tengo varias columnas de TextField en mi objeto UserProfile que contienen objetos JSON. También he definido una propiedad de establecimiento / obtención para cada columna que encapsula la lógica para serializar y deserializar el JSON en estructuras de datos de Python.

La naturaleza de estos datos garantiza que se accederá muchas veces mediante la lógica de la vista y la plantilla dentro de una sola Solicitud. Para ahorrar en costos de deserialización, me gustaría memorizar las estructuras de datos de Python en lectura, invalidar en escritura directa a la propiedad o guardar la señal del objeto modelo.

¿Dónde / cómo almaceno la nota? Estoy nervioso por el uso de variables de instancia, ya que no entiendo la magia detrás de cómo un perfil de usuario en particular es instanciado por una consulta. ¿ __init__ seguro usar __init__ o debo verificar la existencia del atributo memo a través de hasattr() en cada lectura?

Aquí hay un ejemplo de mi implementación actual:

 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) 

Puede estar interesado en un decorador django django.utils.functional.memoize .

Django usa esto para almacenar costosas operaciones como la resolución de URL.

Generalmente, uso un patrón como este:

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

Luego utiliza el método get_expensive_operation para acceder a los datos.

Sin embargo, en su caso particular, creo que se está acercando a esto de manera ligeramente incorrecta. Debe realizar la deserialización cuando el modelo se carga por primera vez desde la base de datos y serializar solo para guardar. Luego, simplemente puede acceder a los atributos como un diccionario estándar de Python cada vez. Puede hacer esto definiendo un tipo JSONField personalizado, subclasificando modelos.TextField, que reemplaza to_python y get_db_prep_save .

De hecho alguien ya lo ha hecho: mira aquí .

Para los métodos de clase, debe usar django.utils.functional.cached_property .

Dado que el primer argumento de un método de clase es self , memoize mantendrá una referencia al objeto y los resultados de la función incluso después de que lo haya desechado. Esto puede causar memory leaks al evitar que el recolector de basura limpie el objeto obsoleto. cached_property convierte la sugerencia de Daniel en un decorador.