Caching something is to save the result of an expensive calculation so that we don’t have to perform the calculation next time. We will discuss the caching in Django REST framework step by step:
- Caching Concept
- Setting up the Memcached
- Adding Cache Config in
settings.py
- Caching API Response
- Invalidating Cached Data
Caching Concept
This is the pseudocode of the general caching concept:
get data from the cache using a key
if the key is in the cache:
return the cached data
else:
generate data
save the generated data in the cache with a key(for next time)
return the generated data
Setting up the Memcached :
Install Memcached :
We will install Memcached using docker image.
docker run -d memcached -p 11211:11211 -m 64
This would set the Memcached server to use 64 megabytes for storage running at 11211 port.
Install a Python Client :
We will install pylibmc, a python client to communicate with the Memcached server. But at first, we need to install the required dependencies of pylibmc
,
For ubuntu
or ubuntu-based docker image,
sudo apt-get install libmemcached-dev zlib1g-dev
For MacOS
,
brew install libmemcached
Then install the PyPI package,
pip install pylibmc
Adding Cache Config in settings.py :
CACHE_HOST='127.0.0.1'
CACHE_PORT=11211
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': f'{CACHE_HOST}:{CACHE_PORT}',
}
}
Caching API Response :
Suppose, We have a user
app. We will have the following functionalities:
- We will implement caching at user details API
/api/user/{id}/
. - Any request at this API will try to get from the cache. If no cached data exists for a user_id, user details data will be fetched from Database.
- Any change or deletion on the User object will invalidate the cache.
The following is the
user details
API,
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from django.core.cache import cache
from apps.user.models import User
from apps.user.serializers import UserSerializer
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def retrieve(self, request, pk=None):
cache_key = f"user_details_{pk}"
data = cache.get(cache_key)
if data:
return Response(data)
response = super().retrieve(request, pk=pk)
cache.set(cache_key, response.data)
return response
Invalidating Cached Data :
We will invalidate the cached data using django signal.
Creating Signal Receivers :
We will add the following lines in user/signals.py
,
def _invalidate_cached_data(user_id):
cache_key = f"user_details_{pk}"
cache.delete(cache_key)
def user_post_save_handler(sender, instance, created, **kwargs):
if not created:
_invalidate_cached_data(instance.id)
def user_post_delete_handler(sender, instance, **kwargs):
_invalidate_cached_data(instance.id)
Connecting Receivers with Signals :
We will add the following line at user/apps.py
inside user app,
from django.apps import AppConfig
from django.db.models.signals import post_save, post_delete
class UserAppConfig(AppConfig):
name = "apps.user"
def ready(self):
from apps.user.models import User
from apps.user.signals import (
user_post_delete_handler,
user_post_save_handler,
)
post_save.connect(user_post_save_handler, sender=User)
post_delete.connect(user_post_delete_handler, sender=User)
Changing default_app_config
:
We will add the following line at user/__init__.py
in user
app.
default_app_config = "apps.user.apps.UserAppConfig"
Feel free to leave a comment.
Top comments (0)