Ответ 1
Создайте пользовательский менеджер для своей модели. В своем методе contribute_to_class
установите сигнал для class_prepared
. Этот сигнал вызывает функцию, которая связывает больше сигналов с моделью.
У меня есть абстрактная модель, которая хранит кеш на диске. Когда я удаляю модель, мне нужно ее удалить кеш. Я хочу, чтобы это произошло для каждой производной модели.
Если я подключу сигнал, определяющий абстрактную модель, это не распространяется на производные модели:
pre_delete.connect(clear_cache, sender=MyAbstractModel, weak=False)
Если я попытаюсь подключить сигнал в init, где я могу получить имя производного класса, он работает, но я боюсь, что он попытается очистить кеш столько раз, сколько я 'инициализировал производную модель, а не только один раз.
Где я должен подключить сигнал?
Создайте пользовательский менеджер для своей модели. В своем методе contribute_to_class
установите сигнал для class_prepared
. Этот сигнал вызывает функцию, которая связывает больше сигналов с моделью.
На основе ответа Джастина Лилли я создал пользовательский менеджер, который связывает сигнал post_save с каждым дочерним элементом класса, будь он абстрактным или нет.
Это одноразовый, плохо протестированный код, так что будьте осторожны! Он работает до сих пор.
В этом примере мы разрешаем абстрактной модели определять CachedModelManager как менеджер, который затем расширяет базовые функции кэширования для модели и ее дочерних элементов. Он позволяет вам определить список летучих ключей, которые должны быть удалены при каждом сохранении (отсюда сигнал post_save), и добавляет пару вспомогательных функций для генерации ключей кеша, а также для извлечения, установки и удаления ключей.
Это, конечно, предполагает, что у вас есть настройка бэкэнд для кеша и правильная работа.
# 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
Патчи приветствуются!
Я думаю, вы можете подключиться к post_delete без указания отправителя, а затем проверить, находится ли фактический отправитель в списке классов модели. Что-то вроде:
def my_handler(sender, **kwargs):
if sender.__class__ in get_models(someapp.models):
...
Очевидно, вам понадобится более сложная проверка и т.д., но вы получите эту идею.