.. _caching: 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 ----------------------- ``_ Views cachen ----------------------- ``_ Teile von Templates cachen ----------------------------- ``_ Low Level Cache Api ----------------------------- ``_ Caching Backends =================== Django unterstützt verschiedene Caching-Backends. * MemCached: https://docs.djangoproject.com/en/stable/topics/cache/#memcached * Datenban: https://docs.djangoproject.com/en/stable/topics/cache/#database-caching * Dateisystem: https://docs.djangoproject.com/en/stable/topics/cache/#filesystem-caching * Local Memory: https://docs.djangoproject.com/en/stable/topics/cache/#local-memory-caching * Dummy Cache: https://docs.djangoproject.com/en/stable/topics/cache/#dummy-caching-for-development * Redis (ab Django 4.x): https://docs.djangoproject.com/en/ref/topics/cache/#redis 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: .. code-block:: python CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } views cachen ------------------------ from django.views.decorators.cache import cache_page .. code-block:: python @cache_page(60 * 15) def my_view(request): ... oder in den URls .. code-block:: python from django.views.decorators.cache import cache_page urlpatterns = [ path('foo//', cache_page(60 * 15)(my_view)), ] oder als Mixin .. code-block:: python 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 .. code-block:: python class EventListView(CacheMixin, ListView): ... Template Caching ------------------ Auch Teile des Templates können gecached werden: .. code-block:: python {% 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: .. code-block:: python {% 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: .. code-block:: python >> 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. .. code-block:: python >>> 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