2010-04-22 12 views
10

Tengo un modelo abstracto que guarda una memoria caché en disco. Cuando elimino el modelo, necesito que elimine el caché. Quiero que esto suceda para todos los modelos derivados también.¿Cómo uso las señales de Django con un modelo abstracto?

Si conecto la señal que especifica el modelo abstracto, esto no se propaga a los modelos derivados:

pre_delete.connect(clear_cache, sender=MyAbstractModel, weak=False) 

Si intento conectar la señal en un init, donde puedo conseguir la clase derivada nombre, funciona, pero me temo que intentará borrar el caché tantas veces como he inicializado un modelo derivado, no solo una vez.

¿Dónde debo conectar la señal?

Respuesta

4

Cree un administrador personalizado para su modelo. En su método contribute_to_class, haga que establezca una señal para class_prepared. Esta señal llama a una función que une más señales al modelo.

+1

Tengo la idea detrás de esto, pero ¿podría elaborar con un ejemplo? Esto realmente ayudaría a mantener las cosas SECAS en mi proyecto actual. –

+0

Editar: Traté de trabajar en su sugerencia en mi propia respuesta ... funciona para mí, pero no estoy 100% seguro! –

+0

No hay un documento oficial para 'contribute_to_class' ... – Raffi

2

Creo que puede conectarse a post_delete sin especificar remitente, y luego verificar si el remitente real está en la lista de clases de modelo. Algo como:

def my_handler(sender, **kwargs): 
    if sender.__class__ in get_models(someapp.models): 
     ... 

Obviamente necesitarás una comprobación más sofisticada, etc., pero ya entiendes.

3

Basándome en la respuesta de Justin Lilly, he creado un administrador personalizado que vincula una señal post_save a cada elemento secundario de una clase, ya sea abstracto o no.

Este es un código único, poco probado, así que ¡cuidado! Sin embargo, funciona hasta ahora.

En este ejemplo, permitimos que un modelo abstracto defina CachedModelManager como administrador, que luego amplía la funcionalidad básica de caché para el modelo y sus elementos secundarios. Le permite definir una lista de claves volátiles que se deben eliminar cada vez que se guarde (de ahí la señal post_save) y agrega un par de funciones auxiliares para generar claves de caché, así como para recuperar, configurar y eliminar claves.

Esto, por supuesto, asume que tiene una configuración de back-end de caché y funciona correctamente.

# helperapp\models.py 
# -*- coding: UTF-8 
from django.db import models 
from django.core.cache import cache 

class CachedModelManager(models.Manager): 
    def contribute_to_class(self, model, name): 
     super(CachedModelManager, self).contribute_to_class(model, name) 

     setattr(model, 'volatile_cache_keys', 
       getattr(model, 'volatile_cache_keys', [])) 

     setattr(model, 'cache_key', getattr(model, 'cache_key', cache_key)) 
     setattr(model, 'get_cache', getattr(model, 'get_cache', get_cache)) 
     setattr(model, 'set_cache', getattr(model, 'set_cache', set_cache)) 
     setattr(model, 'del_cache', getattr(model, 'del_cache', del_cache)) 

     self._bind_flush_signal(model) 

    def _bind_flush_signal(self, model): 
     models.signals.post_save.connect(flush_volatile_keys, model) 

def flush_volatile_keys(sender, **kwargs): 
    instance = kwargs.pop('instance', False) 

    for key in instance.volatile_cache_keys: 
     instance.del_cache(key) 

def cache_key(instance, key): 
    if not instance.pk: 
     name = "%s.%s" % (instance._meta.app_label, instance._meta.module_name) 
     raise models.ObjectDoesNotExist("Can't generate a cache key for " + 
             "this instance of '%s' " % name + 
             "before defining a primary key.") 
    else: 
     return "%s.%s.%s.%s" % (instance._meta.app_label, 
           instance._meta.module_name, 
           instance.pk, key) 

def get_cache(instance, key): 
    result = cache.get(instance.cache_key(key)) 
    return result 

def set_cache(instance, key, value, timeout=60*60*24*3): 
    result = cache.set(instance.cache_key(key), value, timeout) 
    return result 

def del_cache(instance, key): 
    result = cache.delete(instance.cache_key(key)) 
    return result 


# myapp\models.py 
from django.contrib.auth.models import User 
from django.db import models 

from helperapp.models import CachedModelManager 

class Abstract(models.Model): 
    creator = models.ForeignKey(User) 

    cache = CachedModelManager() 

    class Meta: 
     abstract = True 


class Community(Abstract): 
    members = models.ManyToManyField(User) 

    volatile_cache_keys = ['members_list',] 

    @property 
    def members_list(self): 
     result = self.get_cache('members_list') 

     if not result: 
      result = self.members.all() 
      self.set_cache('members_list', result) 

     return result 

Parches welcome!

+0

Convertido en un fragmento: http://djangosnippets.org/snippets/2749/ –

Cuestiones relacionadas