Django Caching

Caching in Django kann auf verschiedenen Ebenen (oder Teilen der Site) implementiert werden. Sie können die gesamte Site oder bestimmte Teile mit verschiedenen Granularitätsstufen zwischenspeichern (in absteigender Reihenfolge der Granularität aufgeführt):

Caching Levels

ganze Webseite cachen

https://docs.djangoproject.com/en/stable/topics/cache/#the-per-site-cache

Views cachen

https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache

Teile von Templates cachen

https://docs.djangoproject.com/en/stable/topics/cache/#template-fragment-caching

Low Level Cache Api

https://docs.djangoproject.com/en/stable/topics/cache/#the-low-level-cache-api

Caching Backends

Django unterstützt verschiedene Caching-Backends.

Für den Produtivbetrieb mit hohem Useraufkommen sollte Redis oder Memcached genutzt werden, da beide auf viele Server skaliert werden können (Redis Cluster).

Caching konfigurieren

in den settings.py kann der Cache mit diversen Werten konfiguriert werden. Hier konfigurieren wir einen Local-Memory-Cache für den Entwicklungsserver:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

views cachen

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

oder in den URls

from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]

oder als Mixin

class CacheMixin:
    cache_timeout = 60

    def get_cache_timeout(self):
        return self.cache_timeout

    def dispatch(self, *a, **k):
        return cache_page(self.get_cache_timeout())(super().dispatch)(*a, **k)

welches dann später in einer klassenbasierten View genutzt werden kann

class EventListView(CacheMixin, ListView):
    ...

Template Caching

Auch Teile des Templates können gecached werden:

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

Hier im Beispiel cachen wir die Sidebar 500 Sekunden, erst danach wird der Block wieder gerendert.

auch in Abhängigkeit von Daten, zum Beispiel in Abhängigeit des Users:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

Low Level Api

Nicht immer wollen wir Templates, Views oder Querysets cachen. Oftmals kommt es vor, dass wir eigene Daten, zum Beispiel aus kostenintensiven Berechungen, für eine Zeitlang aus dem Cache servieren wollen. Dazu bietet Django eine Low Level Api an:

>> from django.core.cache import cache
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None

>>> # wie gewöhnlich kann man bei Dicts auch mit Default-Werten arbeiten,
>>> # falls der Key nicht vorhanden ist.
>>> cache.get('my_key', 'defaultWert')

Manchmal kann es Sinn machen, mit Sentinel-Objekten zur arbeiten.

>>> sentinel = object()
>>> cache.get('my_key', sentinel) is sentinel
False
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key', sentinel) is sentinel
True

Zum Beispiel soll der Key Bank auf None gesetzt werden und 10 Sekunden im Cache bleiben. Würden wir einfach mit get(„bank“) arbeiten, wüssten wir nicht, ob der Value None ist oder der Key gar nicht im Cache vorhanden.

>>> # Cache setzen
>>> cache.set("bank", None, 10)
>>> # es wird None zurückgegeben (weil das der Value ist)
>>> cache.get("bank", sentinel) is sentinel
False
>>> is sentinel, es wird immer noch None
>>> cache.get("bank", sentinel)
False
>>>  # get liefert jetzt als default das sentinel Objekt
>>> # und ist daher nicht mehr im Cache gespeichert
>>> cache.get("bank", sentinel) is sentinel
True