I have an abstract model that keeps an on-disk cache. When I delete the model, I need it to delete the cache. I want this to happen for every derived model as well.
我有一个保持磁盘缓存的抽象模型。当我删除模型时,我需要它来删除缓存。我希望每个派生模型都能实现这一点。
If I connect the signal specifying the abstract model, this does not propagate to the derived models:
如果我连接指定抽象模型的信号,则不会传播到派生模型:
pre_delete.connect(clear_cache, sender=MyAbstractModel, weak=False)
If I try to connect the signal in an init, where I can get the derived class name, it works, but I'm afraid it will attempt to clear the cache as many times as I've initialized a derived model, not just once.
如果我尝试在init中连接信号,在那里我可以获得派生类名,它可以工作,但我担心它会尝试清除缓存,因为我已经多次初始化派生模型,而不是一次。
Where should I connect the signal?
我应该在哪里连接信号?
3 个解决方案
#1
4
Create a custom manager for your model. In its contribute_to_class
method, have it set a signal for class_prepared
. This signal calls a function which binds more signals to the model.
为您的模型创建自定义管理器。在其contrib_to_class方法中,让它为class_prepared设置一个信号。该信号调用将更多信号绑定到模型的函数。
#2
3
I think you can connect to post_delete without specifying sender, and then check if actual sender is in list of model classes. Something like:
我认为您可以在不指定发件人的情况下连接到post_delete,然后检查实际发件人是否在模型类列表中。就像是:
def my_handler(sender, **kwargs):
if sender.__class__ in get_models(someapp.models):
...
Obviously you'll need more sophisticated checking etc, but you get the idea.
显然你需要更复杂的检查等,但你明白了。
#3
3
Building upon Justin Lilly's answer, I've created a custom manager that binds a post_save signal to every child of a class, be it abstract or not.
基于Justin Lilly的回答,我创建了一个自定义管理器,它将post_save信号绑定到一个类的每个子节点,无论是抽象的还是不抽象的。
This is a one-off, poorly tested code, so beware! It works so far, though.
这是一次性的,测试不佳的代码,所以要小心!但它到目前为止工作。
In this example, we allow an abstract model to define CachedModelManager as a manager, which then extends basic caching functionality to the model and its children. It allows you to define a list of volatile keys that should be deleted upon every save (hence the post_save signal) and adds a couple of helper functions to generate cache keys, as well as retrieving, setting and deleting keys.
在此示例中,我们允许抽象模型将CachedModelManager定义为管理器,然后将基本缓存功能扩展到模型及其子项。它允许您定义每次保存时应删除的volatile列表(因此是post_save信号),并添加一些辅助函数来生成缓存键,以及检索,设置和删除键。
This of course assumes you have a cache backend setup and working properly.
这当然假设您有一个缓存后端设置并正常工作。
# 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
Patches welcome!
补丁欢迎!
#1
4
Create a custom manager for your model. In its contribute_to_class
method, have it set a signal for class_prepared
. This signal calls a function which binds more signals to the model.
为您的模型创建自定义管理器。在其contrib_to_class方法中,让它为class_prepared设置一个信号。该信号调用将更多信号绑定到模型的函数。
#2
3
I think you can connect to post_delete without specifying sender, and then check if actual sender is in list of model classes. Something like:
我认为您可以在不指定发件人的情况下连接到post_delete,然后检查实际发件人是否在模型类列表中。就像是:
def my_handler(sender, **kwargs):
if sender.__class__ in get_models(someapp.models):
...
Obviously you'll need more sophisticated checking etc, but you get the idea.
显然你需要更复杂的检查等,但你明白了。
#3
3
Building upon Justin Lilly's answer, I've created a custom manager that binds a post_save signal to every child of a class, be it abstract or not.
基于Justin Lilly的回答,我创建了一个自定义管理器,它将post_save信号绑定到一个类的每个子节点,无论是抽象的还是不抽象的。
This is a one-off, poorly tested code, so beware! It works so far, though.
这是一次性的,测试不佳的代码,所以要小心!但它到目前为止工作。
In this example, we allow an abstract model to define CachedModelManager as a manager, which then extends basic caching functionality to the model and its children. It allows you to define a list of volatile keys that should be deleted upon every save (hence the post_save signal) and adds a couple of helper functions to generate cache keys, as well as retrieving, setting and deleting keys.
在此示例中,我们允许抽象模型将CachedModelManager定义为管理器,然后将基本缓存功能扩展到模型及其子项。它允许您定义每次保存时应删除的volatile列表(因此是post_save信号),并添加一些辅助函数来生成缓存键,以及检索,设置和删除键。
This of course assumes you have a cache backend setup and working properly.
这当然假设您有一个缓存后端设置并正常工作。
# 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
Patches welcome!
补丁欢迎!