Model manager with row caching

RowCacheManager is a model manager that will try to fetch any 'get' (i.e., single-row) requests from the cache. ModelWithCaching is an abstract base model that does some extra work that you'll probably want if you're using the RowCacheManager. So to use this code, you just need to do two things: First, set objects=RowCacheManager() in your model definition, then inherit from ModelWithCaching if you want the invalidation for free. If you are unusually brave, use the metaclass for ModelWithCaching and you won't even need the "objects=RowCacheManager()" line.

fromdjango.confimportsettingsfromdjango.core.cacheimportcachefromdjango.dbimportmodelsCACHE_PREFIX=settings.CACHE_MIDDLEWARE_KEY_PREFIXCACHE_EXPIRE=30defcache_key(model,id):return('%s:%s:%s'%(CACHE_PREFIX,model._meta.db_table,id)).replace(' ','')classRowCacheManager(models.Manager):"""Manager for caching single-row queries. To make invalidation easy, we use an extra layer of indirection. The query arguments are used as a cache key, whose stored value is the unique cache key pointing to the object. When a model using RowCacheManager is saved, this unique cache should be invalidated. Doing two memcached queries is still much faster than fetching from the database."""defget(self,*args,**kwargs):# TODO: skip the layer of indirection if 'kwargs' contains id or pk?id=repr(kwargs)pointer_key=cache_key(self.model,id)model_key=cache.get(pointer_key)ifmodel_keyisnotNone:model=cache.get(model_key)ifmodelisnotNone:returnmodel# One of the cache queries missed, so we have to get the object from the database:model=super(RowCacheManager,self).get(*args,**kwargs)ifnotmodel_key:model_key=cache_key(model,model.pk)cache.set(pointer_key,model_key,CACHE_EXPIRE)cache.set(model_key,model,CACHE_EXPIRE)# you can use 'add' instead of 'set' herereturnmodelclassModelWithCaching(models.Model):defsave(self,*args,**kwargs):super(ModelWithCaching,self).save()ifkwargs.pop('invalidate_cache',True):cache.delete(cache_key(self,self.id))classMeta:abstract=True############################# Cross this line at your own risk__metaclass__=MetaCachingModelBase=type(models.Model)classMetaCaching(ModelBase):"""Sets ``objects'' on any model that inherits from ModelWithCaching to be a RowCacheManager. This is tightly coupled to Django internals, so it could break if you upgrade Django. This was done partially as a proof-of- concept. It is advised to only use code above the comment line."""def__new__(*args,**kwargs):new_class=ModelBase.__new__(*args,**kwargs)new_manager=RowCacheManager()new_manager.contribute_to_class(new_class,'objects')new_class._default_manager=new_managerreturnnew_class