1. Jason Moiron
  2. johnny-cache
Issue #32 resolved

Invalidate tables to which foreign keys refer

svarog
created an issue

Example: We have two models //Product// and //TopProduct//, //TopProduct.product// refer to //Product// instance. After deleting this //Product// instance //TopProduct// is deleted too and cache for //TopProduct// is invalid.

Solution: Add to //cache.QueryCacheBackend// method like this and register it as //post_delete()// signal handler:

{{{ def invalidate_fk(self, instance, **kwargs): if self._patched: tables = {instance._meta.db_table: None} self.keyhandler.invalidate_table(instance._meta.db_table) for obj in instance._meta._related_objects_cache.keys(): obj_table = obj.model._meta.db_table if not tables.has_key(obj_table): tables[obj_table] = None self.keyhandler.invalidate_table(obj_table)

def _handle_signals(self): ... signals.post_delete.connect(self.invalidate_fk, sender=None) ... }}}

Comments (3)

  1. panni

    Well, this actually doesn't have to be monkey-patched:

    class ExtendedJCQueryCacheBackend(QueryCacheBackend):
        """ 
        extended johnny's querycachebackend with the functionality of ticket #32 (https://bitbucket.org/jmoiron/johnny-cache/issue/32/invalidate-tables-to-which-foreign-keys)
        """
        def invalidate_fk(self, instance, **kwargs):
            if self._patched:
                tables = {instance._meta.db_table: None}
                self.keyhandler.invalidate_table(instance._meta.db_table)
                for obj in instance._meta._related_objects_cache.keys():
                    obj_table = obj.model._meta.db_table
                    if not tables.has_key(obj_table):
                        tables[obj_table] = None
                        self.keyhandler.invalidate_table(obj_table)
                        
        def _handle_signals(self):
            super(ExtendedJCQueryCacheBackend, self)._handle_signals()
            
            from django.db.models import signals
            signals.post_delete.connect(self.invalidate_fk, sender=None)
    
    
    class ExtendedJCQueryCacheMiddleware(QueryCacheMiddleware):
        """
        middleware using the extended QueryCacheBackend from above
        """
        
        __state = {}
        def __init__(self):
            self.__dict__ = self.__state
            self.disabled = johnnySettings.DISABLE_QUERYSET_CACHE
            self.installed = getattr(self, 'installed', False)
            if not self.installed and not self.disabled:
                # when we install, lets refresh the blacklist, just in case johnny
                # was loaded before the setting exists somehow...
                johnnyCache.blacklist = johnnySettings.BLACKLIST
                self.query_cache_backend = ExtendedJCQueryCacheBackend(cache_backend=None, keyhandler=None, keygen=None)
                self.query_cache_backend.patch()
                self.installed = True
    

    and use "ExtendedJCQueryCacheMiddleware" as middleware instead of the default one.

  2. Log in to comment