Django bied geweldige tools voor caching eenvoudig maken. Het heeft naar mijn mening wel 2 grote beperkingen:
- Het stuurt automatisch Cache-control headers, waardoor de client’s cache verhinderd dat de pagina goed herlaad als de taal gewijzigd word en er geen taal prefix in de url gebruikt word
- Het cached niet per gebruiker, waardoor als we niet opletten er mogelijks private informatie gelekt kan worden.
Beide van deze beperkingen waren oorspronkelijk een probleem bij het herschrijven van Skyz. Het eerste probleem kon deels verholpen worden door taalprefixes toe te voeren aan de url’s, de andere problemen kon ik oplossen op de volgende manieren.
Cache control headers verwijderen
Het eerste probleem kan opgelost worden door de @cache_page decorator te wrappen in een @never_cache decorator (door deze boven de @cache_page decorator te plaatsen zoals ik in een vorige post heb toegelicht), of met een stukje middleware om nooit pagina’s te cachen
# app/middleware.py from django.utils.cache import add_never_cache_headers def disable_client_side_caching_middleware(get_response): def middleware(request): response = get_response(request) add_never_cache_headers(response) return response return middleware
Als we deze middleware activeren, zal een pagina nooit gecached worden. Het is wel belangrijk om op te merken dat een downstream cache (zoals varnish) dan ook nooit een pagina zal cachen.
Per user caching
Het tweede probleem is iets moeilijker om op te lossen. Ik heb het opgelost door aangemelde gebruikers een ongecachede versie te tonen, maar anonieme gebruikers wel een gecachte versie te tonen.
Om dit te doen heb ik een decorator-wrapper geschreven:
# app/decorators.py def conditional_cache(decorator): def _decorator(view): decorated_view = decorator(view) # This holds the view with cache decorator def _view(request, *args, **kwargs): if request.user.is_authenticated: # If user is staff return view(request, *args, **kwargs) else: return decorated_view(request, *args, **kwargs) return _view return _decorator
We gebruiken nu in plaats van de @cache_page decorator het volgende:
@conditional_cache(decorator=cache_page(300)) def view(request): # do stuff =)