Skip to content

Instantly share code, notes, and snippets.

@mattwilliamson
Created November 4, 2011 20:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattwilliamson/1340368 to your computer and use it in GitHub Desktop.
Save mattwilliamson/1340368 to your computer and use it in GitHub Desktop.
Metaclass Django Model Instance Caching
import logging
import uuid
import re
from decimal import Decimal
from django.conf import settings
from django.db import models
from django.db.models import signals
from django.dispatch import receiver
from django.core.cache import cache
def post_save_cache(sender, instance, signal, *args, **kwargs):
"""This is called when any BaseModel subclass instance is saved. Then we cache the instance."""
logging.debug('Saving %s instance to cache.' % instance.__class__.__name__)
# Store full instance under SID-based Key
cache.set(instance.get_cache_key_sid(instance.sid), instance, settings.DEFAULT_INSTANCE_CACHE_TIME)
# Store SID under PK-based key
cache.set(instance.get_cache_key_pk(instance.pk), instance, settings.DEFAULT_INSTANCE_CACHE_TIME)
class InstanceCachable(models.Model.__metaclass__):
"""Meta class for Models.
Used to setup post/pre-save signals for caching all models that inherit from BaseModel"""
def __init__(cls, name, bases, dct):
super(InstanceCachable, cls).__init__(name, bases, dct)
signals.post_save.connect(post_save_cache, sender=cls)
class BaseModel(models.Model):
"""Contains some basic fields for most models to contain"""
__metaclass__ = InstanceCachable
class Meta:
abstract = True
ordering = ('-date_created',)
sid_prefix = ''
date_created = models.DateTimeField(blank=True, null=True, auto_now_add=True)
date_updated = models.DateTimeField(blank=True, null=True, auto_now=True)
sid = models.CharField(max_length=34, db_index=True, unique=True, blank=True, null=True)
friendly_name = models.CharField(max_length=64, blank=True, default='')
def __unicode__(self):
return self.friendly_name or self.sid
def generate_sid(self):
self.sid = str(getattr(self, 'sid_prefix', self.__class__.__name__))[:2] + uuid.uuid4().hex
def save(self, *args, **kwargs):
if self.sid in ('', None):
self.generate_sid()
super(BaseModel, self).save(*args, **kwargs)
@classmethod
def get_by_sid(cls, sid):
return cls.object.get(sid=sid)
@classmethod
def get_cache_key_sid(cls, sid):
"""Returns a string used as a caching key. SID-based."""
return 'Instance.sid.%s.%s' % (cls.__name__, sid)
@classmethod
def get_cache_key_pk(cls, pk):
"""Returns a string used as a caching key. Primary key-based."""
return 'Instance.pk.%s.%s' % (cls.__name__, pk)
@classmethod
def get_by_sid(cls, sid):
logging.debug('Fetching %s from cache with sid "%s"' % (cls.__name__, sid))
key = cls.get_cache_key_sid(sid)
instance = cache.get()
# If not in the cache, fetch from db and put into cache
if not instance:
logging.debug('Not in cache. Fetching from DB.')
instance = cls.objects.get(sid=sid)
post_save_cache(cls, instance, signals.post_save)
return instance
@classmethod
def get_by_pk(cls, pk):
logging.debug('Fetching %s from cache with primary key "%s"' % (cls.__name__, pk))
key = cls.get_cache_key_pk(pk)
instance = cache.get()
# If not in the cache, fetch from db and put into cache
if not instance:
logging.debug('Not in cache. Fetching from DB.')
instance = cls.objects.get(pk=pk)
post_save_cache(cls, instance, signals.post_save)
return instance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment