InstanceCache¶
To cache single instance of a model on some (one or more) fields of same model,
you have to define a class derived from flash.InstanceCache class in
caches.py file inside your app.
Suppose you want to cache User model’s instance on username
field. Then you have to create class:
from flash import InstanceCache
from django.contrib.auth.models import User
class UserCacheOnUsername(InstanceCache):
model = User
key_fields = ('username',)
Here, we have created a class UserCacheOnUsername derived from
InstanceCache with attributes model and key_fields.
It’s mandatory for each type of cache class to define these attributes.
model tells whose fields will be given to get the instance.
key_fields tells what parameters will be given to get result from memcached.
These parameters must be the fields of same model. If value is not found in memcached then these are used to retrieve the result from database.
In case of InstanceCache, the model is the same whose instance will get cached.
Use case¶
Here’s the code to use our newly created cache class:
username = 'bill1992'
user = UserCacheOnUsername(username).resolve()
# or
user = UserCacheOnUsername(username=username).resolve()
UserCacheOnUsername(username) create the cache query.
Calling .resolve() method on it tries to get user from
memcached, if not found then fetches it from database and sets it to memcached.
If user is not found then it will raise User.DoesNotExist exception,
which is similar in behaviour if we’d written:
# Db query
user = User.objects.get(username=username)
Using cache manager¶
You can also use cache manager on User to obtain the instance
which looks very similar to Django’s get query syntax:
user = User.cache.get(username=username)
You will soon get to know about it when we come to ModelCacheManager
section.
There are methods get_or_none and get_or_404 on cache
manager which you can also use:
# It returns User's instance if found otherwise returns None
user = User.cache.get_or_none(username=username)
# It raises Http404 exception if instance not found
user = User.cache.get_or_404(username=username)
Override get_instance¶
The current behaviour of InstanceCache derived classes is to use
<model>.objects.get() on given parameters as fallback method if
value not found in memcached.
You can override this behaviour by defining get_instance
method in the class.
E.g. there’s Avatar model defined like:
class Avatar(models.Model):
user = models.ForeignKey(User)
file_path = models.FileField()
primary = models.BooleanField()
And you want to cache primary avatar instnace on user. Then you do it by
class PrimaryAvatarCacheOnUser(InstanceCache):
model = Avatar
key_fields = ('user',)
def get_instance(self, user):
avatars = self.get_queryset().filter(user=user, primary=True)
if avatars:
return avatars[0]
return None
# Use above cache class
avatar = PrimaryAvatarCacheOnUser(user=user).resolve()
Here, self.get_queryset() will return a queryset on Avatar
model. It takes care of which db to make query on.
In this case, this cache class will never raise Avatar.DoesNotExist
exception since it is setting None in memcached against the key
when primary avatar not found.
More about key_fields¶
Till now we defined cache classes having key_fields with one field only. So here is an example where more than one fields are used to create key for cache:
class ParticipationCacheOnUserEvent(InstanceCache):
model = Participation
# Since only one Participation instance exists for
# a user and an event
key_fields = ('user', 'event')
And here are the different ways to use this cache class
# If parameters given as args, taken in same order of key_fields
participation = ParticipationCacheOnUserEvent(user, event).resolve()
# Parameters can be given in hibrid form too (args & kwargs)
participation = ParticipationCacheOnUserEvent(user, event=event).resolve()
# Parameters can be given in any order if given as kwargs
participation = ParticipationCacheOnUserEvent(event=event, user=user).resolve()
# Parameters must be given as kwargs when using cache manager
participation = Participation.cache.get(user=user, event=event)
Even if you have id of any related field, you can pass them instead of instance. So this will work
participation = Participation.cache.get(user=user, event_id=event_id)
Some notes:
- Names of cache classes should be unique because cache keys are made using that name.
- Don’t use related fields’s attname as key_fields though those are which
gets used in db table. E.g. in above example,
you should not use
user_idorevent_idin key_fields. - When defining custom
get_instancemethod, neither the order nor the name of key_fields should be altered. - In case of
InstanceCacheandQuerysetCache, you can put GenericForeignKey field’s name in key_fields.