16

ディスク上のキャッシュを保持する抽象モデルがあります。モデルを削除するとき、キャッシュを削除するためにモデルが必要です。これは、すべての派生モデルでも発生するようにしたいと思います。

抽象モデルを指定する信号を接続すると、これは派生モデルに伝播されません。

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

派生クラス名を取得できるinitでシグナルを接続しようとすると、機能しますが、派生モデルを初期化した回数だけでなく、何度もキャッシュをクリアしようとするのではないかと心配しています。 。

信号はどこに接続すればよいですか?

4

3 に答える 3

10

Justin Abrahmsの回答に基づいて、抽象的かどうかに関係なく、post_saveシグナルをクラスのすべての子にバインドするカスタムマネージャーを作成しました。

これは1回限りの、テストが不十分なコードであるため、保証はありません。しかし、それはうまくいくようです。

この例では、抽象モデルをマネージャーとして定義できるようにしますCachedModelManager。マネージャーは、基本的なキャッシュ機能をモデルとその子に拡張します。volatile_cache_keysこれにより、保存のたびに削除する必要のある揮発性キー(と呼ばれるクラス属性)のリストを定義でき(したがって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

パッチは大歓迎です!

于 2012-05-15T16:14:28.390 に答える
8

送信者を指定せずにpost_deleteに接続して、実際の送信者がモデルクラスのリストに含まれているかどうかを確認できると思います。何かのようなもの:

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

明らかに、より高度なチェックなどが必要になりますが、アイデアは得られます。

于 2010-04-22T20:53:17.047 に答える
5

モデルのカスタムマネージャーを作成します。そのcontribute_to_class方法では、の信号を設定しclass_preparedます。このシグナルは、より多くのシグナルをモデルにバインドする関数を呼び出します。

于 2010-04-22T18:26:04.237 に答える