Djangos ramverk för cache¶
En grundläggande avvägning när det gäller dynamiska webbplatser är att de är just dynamiska. Varje gång en användare begär en sida gör webbservern alla möjliga beräkningar - från databasfrågor till mallåtergivning till affärslogik - för att skapa den sida som besökaren på din webbplats ser. Det här är mycket dyrare, sett ur ett processoverheadperspektiv, än en standardserver som läser en fil från filsystemet.
För de flesta webbapplikationer är denna overhead inte en stor sak. De flesta webbapplikationer är inte washingtonpost.com
eller lashdot.org
; de är små till medelstora webbplatser med so-so trafik. Men för webbplatser med medelhög till hög trafik är det viktigt att skära så mycket overhead som möjligt.
Det är där cachelagring kommer in i bilden.
Att cachelagra något är att spara resultatet av en dyr beräkning så att du inte behöver utföra beräkningen nästa gång. Här är lite pseudokod som förklarar hur detta skulle fungera för en dynamiskt genererad webbsida:
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
Django levereras med ett robust cachesystem som låter dig spara dynamiska sidor så att de inte behöver beräknas för varje begäran. För enkelhetens skull erbjuder Django olika nivåer av cachegranularitet: Du kan cachelagra resultatet av specifika vyer, du kan cachelagra endast de delar som är svåra att producera, eller du kan cachelagra hela din webbplats.
Django fungerar också bra med ”nedströms” cacher, till exempel Squid och webbläsarbaserade cacher. Det här är de typer av cacheminnen som du inte direkt kontrollerar men som du kan ge ledtrådar (via HTTP-rubriker) om vilka delar av din webbplats som ska cachas och hur.
Se även
I Cache Framework design philosophy förklaras några av ramverkets designbeslut.
Uppsättning av cachen¶
Cachesystemet kräver en liten del inställningar. Du måste nämligen tala om var dina cachade data ska finnas - om det är i en databas, på filsystemet eller direkt i minnet. Det här är ett viktigt beslut som påverkar cachens prestanda; ja, vissa cachetyper är snabbare än andra.
Dina cachepreferenser anges i inställningen CACHES
i din inställningsfil. Här följer en förklaring av alla tillgängliga värden för CACHES
.
Memcached¶
Memcached är en helt minnesbaserad cache-server som ursprungligen utvecklades för att hantera höga belastningar på LiveJournal.com och därefter open-sourcades av Danga Interactive. Den används av webbplatser som Facebook och Wikipedia för att minska databasåtkomsten och dramatiskt öka webbplatsens prestanda.
Memcached körs som en daemon och tilldelas en viss mängd RAM-minne. Allt den gör är att tillhandahålla ett snabbt gränssnitt för att lägga till, hämta och ta bort data i cacheminnet. All data lagras direkt i minnet, så det finns ingen overhead för databas- eller filsystemanvändning.
Efter att ha installerat Memcached själv måste du installera en Memcached-bindning. Det finns flera Python Memcached-bindningar tillgängliga; de två som stöds av Django är pylibmc och pymemcache.
Att använda Memcached med Django:
Ställ in
BACKEND
tilldjango.core.cache.backends.memcached.PyMemcacheCache
ellerdjango.core.cache.backends.memcached.PyLibMCCache
(beroende på vilken memcached-bindning du har valt)Ställ in
LOCATION
tillip:port
-värden, därip
är IP-adressen för Memcached-daemon ochport
är den port som Memcached körs på, eller till ettunix:path
-värde, därpath
är sökvägen till en Memcached Unix-socketfil.
I det här exemplet körs Memcached på localhost (127.0.0.1) port 11211, med bindningen pymemcache
:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
}
}
I det här exemplet är Memcached tillgängligt via en lokal Unix-socketfil /tmp/memcached.sock
med bindningen pymemcache
:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "unix:/tmp/memcached.sock",
}
}
En utmärkt funktion hos Memcached är dess förmåga att dela en cache över flera servrar. Det innebär att du kan köra Memcached-daemoner på flera maskiner och programmet kommer att behandla gruppen av maskiner som en enkel cache, utan att behöva duplicera cache-värden på varje maskin. För att dra nytta av den här funktionen måste du inkludera alla serveradresser i LOCATION
, antingen som en semikolon- eller kommaseparerad sträng eller som en lista.
I det här exemplet delas cachen mellan Memcached-instanser som körs på IP-adresserna 172.19.26.240 och 172.19.26.242, båda på port 11211:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": [
"172.19.26.240:11211",
"172.19.26.242:11211",
],
}
}
I följande exempel delas cacheminnet mellan Memcached-instanser som körs på IP-adresserna 172.19.26.240 (port 11211), 172.19.26.242 (port 11212) och 172.19.26.244 (port 11213):
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": [
"172.19.26.240:11211",
"172.19.26.242:11212",
"172.19.26.244:11213",
],
}
}
Som standard ställer backend PyMemcacheCache
in följande alternativ (du kan åsidosätta dem i din OPTIONS
):
"OPTIONS": {
"allow_unicode_keys": True,
"default_noreply": False,
"serde": pymemcache.serde.pickle_serde,
}
En sista punkt om Memcached är att minnesbaserad cachelagring har en nackdel: eftersom den cachelagrade datan lagras i minnet kommer datan att gå förlorad om din server kraschar. Det är uppenbart att minnet inte är avsett för permanent datalagring, så lita inte på minnesbaserad cachning som din enda datalagring. Utan tvekan bör * ingen * av Djangos cachelagringsbackends användas för permanent lagring - de är alla avsedda att vara lösningar för cachelagring, inte lagring - men vi påpekar detta här eftersom minnesbaserad cachelagring är särskilt tillfällig.
Redis¶
Redis är en databas i minnet som kan användas för cachelagring. För att börja behöver du en Redis-server som körs antingen lokalt eller på en fjärrmaskin.
När du har konfigurerat Redis-servern måste du installera Python-bindningar för Redis. redis-py är den bindning som stöds naturligt av Django. Installation av paketet hiredis-py rekommenderas också.
För att använda Redis som cache-backend med Django:
Ställ in
BACKEND
tilldjango.core.cache.backends.redis.RedisCache
.Ställ in
LOCATION
till URL:en som pekar till din Redis-instans, med hjälp av lämpligt schema. Seredis-py
-dokumenten för `detaljer om de tillgängliga schemana <https://redis-py.readthedocs.io/en/stable/connections.html#redis.connection.ConnectionPool.from_url>`_.
Till exempel, om Redis körs på localhost (127.0.0.1) port 6379:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
}
}
Ofta är Redis-servrar skyddade med autentisering. För att ange ett användarnamn och lösenord, lägg till dem i LOCATION
tillsammans med URL:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://username:password@127.0.0.1:6379",
}
}
Om du har konfigurerat flera Redis-servrar i replikationsläget kan du ange servrarna antingen som en semikolon- eller kommaseparerad sträng eller som en lista. När du använder flera servrar utförs skrivoperationer på den första servern (leader). Läsoperationer utförs på de andra servrarna (replikerna) som väljs slumpmässigt:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": [
"redis://127.0.0.1:6379", # leader
"redis://127.0.0.1:6378", # read-replica 1
"redis://127.0.0.1:6377", # read-replica 2
],
}
}
Cachelagring av databas¶
Django kan lagra sina cachade data i din databas. Detta fungerar bäst om du har en snabb, välindexerad databasserver.
För att använda en databastabell som cache-backend:
Ställ in
BACKEND
tilldjango.core.cache.backends.db.DatabaseCache
Ställ in
LOCATION
tilltablename
, namnet på databastabellen. Detta namn kan vara vad du vill, så länge det är ett giltigt tabellnamn som inte redan används i din databas.
I det här exemplet är cachetabellens namn my_cache_table
:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "my_cache_table",
}
}
Till skillnad från andra cache-backends har databascachen inte stöd för automatisk gallring av utgångna poster på databasnivå. Istället gallras utgångna cacheposter varje gång add()
, set()
eller touch()
anropas.
Skapa cache-tabellen¶
Innan du använder databascachen måste du skapa cachetabellen med det här kommandot:
python manage.py createcachetable
Detta skapar en tabell i din databas som är i det korrekta format som Djangos databas-cache-system förväntar sig. Namnet på tabellen hämtas från LOCATION
.
Om du använder flera databascacher skapar createcachetable
en tabell för varje cache.
Om du använder flera databaser följer createcachetable
metoden allow_migrate()
i dina databasroutrar (se nedan).
Precis som migrate
kommer createcachetable
inte att röra en befintlig tabell. Den kommer bara att skapa tabeller som saknas.
Om du vill skriva ut den SQL som ska köras, i stället för att köra den, använder du alternativet createcachetable --dry-run
.
Flera databaser¶
Om du använder databascache med flera databaser måste du också ställa in routningsinstruktioner för din databascachetabell. När det gäller routing visas databasens cachetabell som en modell med namnet CacheEntry
, i en applikation med namnet django_cache
. Denna modell kommer inte att visas i modellcachen, men modellinformationen kan användas för routingändamål.
Följande router skulle till exempel styra alla läsoperationer i cachen till cache_replica
och alla skrivoperationer till cache_primary
. Cachetabellen kommer endast att synkroniseras till cache_primary
:
class CacheRouter:
"""A router to control all database cache operations"""
def db_for_read(self, model, **hints):
"All cache read operations go to the replica"
if model._meta.app_label == "django_cache":
return "cache_replica"
return None
def db_for_write(self, model, **hints):
"All cache write operations go to primary"
if model._meta.app_label == "django_cache":
return "cache_primary"
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"Only install the cache model on primary"
if app_label == "django_cache":
return db == "cache_primary"
return None
Om du inte anger några routningsanvisningar för databasens cachemodell kommer cache-backend att använda standarddatabasen.
Och om du inte använder databascache-backend behöver du inte oroa dig för att tillhandahålla routningsinstruktioner för databascachemodellen.
Cachelagring i filsystem¶
Den filbaserade backend serialiserar och lagrar varje cache-värde som en separat fil. För att använda denna backend, sätt BACKEND
till "django.core.cache.backends.filebased.FileBasedCache"
och LOCATION
till en lämplig katalog. Till exempel, för att lagra cachad data i /var/tmp/django_cache
, använd denna inställning:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache",
}
}
Om du använder Windows sätter du enhetsbeteckningen i början av sökvägen, så här:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "c:/foo/bar",
}
}
Katalogsökvägen ska vara absolut, dvs. den ska börja vid roten av filsystemet. Det spelar ingen roll om du lägger till ett snedstreck i slutet av inställningen.
Se till att katalogen som den här inställningen pekar på antingen finns och är läsbar och skrivbar, eller att den kan skapas av systemanvändaren som din webbserver körs under. Om du fortsätter med exemplet ovan och din server körs som användaren apache
måste du se till att katalogen /var/tmp/django_cache
finns och är läsbar och skrivbar av användaren apache
, eller att den kan skapas av användaren apache
.
Varning
När cacheminnet LOCATION
ingår i MEDIA_ROOT
, STATIC_ROOT
eller STATICFILES_FINDERS
kan känsliga data exponeras.
En angripare som får tillgång till cachefilen kan inte bara förfalska HTML-innehåll, som din webbplats kommer att lita på, utan också fjärrköra godtycklig kod, eftersom data serialiseras med pickle
.
Varning
Filsystemets cachelagring kan bli långsam när ett stort antal filer lagras. Om du stöter på detta problem bör du överväga att använda en annan cachemekanism. Du kan också underklassa FileBasedCache och förbättra gallringsstrategin.
Cachelagring i lokalt minne¶
Detta är standardcachen om ingen annan anges i din inställningsfil. Om du vill ha hastighetsfördelarna med cachning i minnet men inte har möjlighet att köra Memcached kan du överväga backend för cache i lokalt minne. Denna cache är per-process (se nedan) och trådsäker. För att använda den, ställ in BACKEND
till "django.core.cache.backends.locmem.LocMemCache"
. Till exempel:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake",
}
}
Cacheminnet LOCATION
används för att identifiera enskilda minneslagringar. Om du bara har en locmem
-cache kan du utelämna LOCATION
; men om du har mer än en lokal minnescache måste du tilldela ett namn till minst en av dem för att hålla dem åtskilda.
Cachen använder en LRU-strategi (least-recently-used) för gallring.
Observera att varje process kommer att ha sin egen privata cache-instans, vilket innebär att ingen cachelagring över processgränserna är möjlig. Detta innebär också att den lokala minnescachen inte är särskilt minneseffektiv, så det är förmodligen inte ett bra val för produktionsmiljöer. Det är trevligt för utveckling.
Dummy-cachelagring (för utveckling)¶
Slutligen levereras Django med en ”dummy”-cache som faktiskt inte cachelagrar - den implementerar bara cache-gränssnittet utan att göra någonting.
Detta är användbart om du har en produktionswebbplats som använder kraftig cachelagring på olika ställen men en utvecklings-/testmiljö där du inte vill cachelagra och inte vill behöva ändra din kod för att specialanpassa det senare. För att aktivera dummy-caching, ställ in BACKEND
så här:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
}
}
Använda en anpassad cache-backend¶
Även om Django inkluderar stöd för ett antal cache-backends direkt, kanske du ibland vill använda en anpassad cache-backend. För att använda en extern cache-backend med Django, använd Python-importsökvägen som BACKEND
i CACHES
-inställningen, så här:
CACHES = {
"default": {
"BACKEND": "path.to.backend",
}
}
Om du bygger din egen backend kan du använda standardcache-backends som referensimplementeringar. Du hittar koden i katalogen django/core/cache/backends/ i Django-källan.
Obs: Utan en riktigt tvingande anledning, till exempel en värd som inte stöder dem, bör du hålla dig till de cache-backends som ingår i Django. De har testats väl och är väldokumenterade.
Cache-argument¶
Varje cache-backend kan ges ytterligare argument för att styra cachningens beteende. Dessa argument anges som ytterligare nycklar i inställningen CACHES
. Giltiga argument är följande:
:inställning:`TIMEOUT <CACHES-TIMEOUT>`: Standardtimeout i sekunder som ska användas för cacheminnet. Standardvärdet för detta argument är
300
sekunder (5 minuter). Du kan ställa inTIMEOUT
tillNone
så att cache-nycklarna som standard aldrig går ut. Ett värde på0
gör att nycklar omedelbart upphör att gälla (i praktiken ”cachas inte”).OPTIONS
: Alla alternativ som ska skickas till cache-backend. Listan över giltiga alternativ varierar med varje backend, och cache-backends som backas upp av ett tredjepartsbibliotek skickar sina alternativ direkt till det underliggande cachebiblioteket.Cache-backends som implementerar sin egen gallringsstrategi (dvs. backends för
locmem
,filsystem
ochdatabas
) kommer att respektera följande alternativ:MAX_ENTRIES
: Det maximala antalet poster som tillåts i cacheminnet innan gamla värden raderas. Standardvärdet för detta argument är300
.CULL_FREQUENCY
: Den andel av posterna som gallras närMAX_ENTRIES
har uppnåtts. Det faktiska förhållandet är1 / CULL_FREQUENCY
, så sättCULL_FREQUENCY
till2
för att gallra hälften av posterna närMAX_ENTRIES
nås. Detta argument bör vara ett heltal och standardvärdet är3
.Ett värde på
0
förCULL_FREQUENCY
innebär att hela cachen kommer att dumpas närMAX_ENTRIES
nås. På vissa backends (i synnerhetdatabasen
) gör detta utrensningen mycket snabbare på bekostnad av fler cachemissar.
Backends för Memcached och Redis skickar innehållet i
OPTIONS
som nyckelordsargument till klientkonstruktörerna, vilket möjliggör mer avancerad kontroll av klientbeteendet. För exempel på användning, se nedan.KEY_PREFIX
: En sträng som automatiskt kommer att inkluderas (prepended som standard) till alla cache-nycklar som används av Django-servern.Se cache-dokumentationen för mer information.
:inställning:`VERSION <CACHES-VERSION>`: Standardversionsnummer för cache-nycklar som genereras av Django-servern.
Se cache-dokumentationen för mer information.
KEY_FUNCTION
En sträng som innehåller en prickad sökväg till en funktion som definierar hur ett prefix, en version och en nyckel ska sättas samman till en slutlig cache-nyckel.Se cache-dokumentationen för mer information.
I det här exemplet konfigureras en backend för ett filsystem med en timeout på 60 sekunder och en maximal kapacitet på 1000 objekt:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache",
"TIMEOUT": 60,
"OPTIONS": {"MAX_ENTRIES": 1000},
}
}
Här följer ett exempel på konfiguration för en pylibmc
-baserad backend som aktiverar det binära protokollet, SASL-autentisering och beteendeläget ketama
:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
"LOCATION": "127.0.0.1:11211",
"OPTIONS": {
"binary": True,
"username": "user",
"password": "pass",
"behaviors": {
"ketama": True,
},
},
}
}
Här är ett exempel på konfiguration för en pymemcache
-baserad backend som aktiverar klientpoolning (vilket kan förbättra prestanda genom att hålla klienter anslutna), behandlar memcache/nätverksfel som cachemissar och ställer in flaggan TCP_NODELAY
på anslutningens socket:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
"OPTIONS": {
"no_delay": True,
"ignore_exc": True,
"max_pool_size": 4,
"use_pooling": True,
},
}
}
Här är ett exempel på konfiguration för en redis
-baserad backend som väljer databas 10
(som standard levereras Redis med 16 logiska databaser) och anger en anpassad connection pool class (redis.ConnectionPool
används som standard):
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"db": "10",
"pool_class": "redis.BlockingConnectionPool",
},
}
}
Cache för varje webbplats¶
När cachen är konfigurerad är det enklaste sättet att använda cachelagring att cachelagra hela din webbplats. Du måste lägga till 'django.middleware.cache.UpdateCacheMiddleware'
och 'django.middleware.cache.FetchFromCacheMiddleware'
till din MIDDLEWARE
-inställning, som i det här exemplet:
MIDDLEWARE = [
"django.middleware.cache.UpdateCacheMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.cache.FetchFromCacheMiddleware",
]
Observera
Nej, det är inte ett skrivfel: ”update”-medievaran måste vara först i listan och ”fetch”-medievaran måste vara sist. Detaljerna är lite otydliga, men se Order of MIDDLEWARE nedan om du vill ha hela historien.
Lägg sedan till följande nödvändiga inställningar i din Django-inställningsfil:
CACHE_MIDDLEWARE_ALIAS
– Det cache-alias som ska användas för lagring.CACHE_MIDDLEWARE_SECONDS
– Heltal antal sekunder som varje sida ska cachas.CACHE_MIDDLEWARE_KEY_PREFIX
– Om cacheminnet delas mellan flera webbplatser som använder samma Django-installation, sätt detta till namnet på webbplatsen eller någon annan sträng som är unik för denna Django-instans, för att förhindra nyckelkollisioner. Använd en tom sträng om du inte bryr dig.
FetchFromCacheMiddleware
cachelagrar GET- och HEAD-svar med status 200, där begäran och svarshuvudena tillåter det. Svar på förfrågningar om samma URL med olika frågeparametrar betraktas som unika sidor och cachelagras separat. Detta middleware förväntar sig att en HEAD-begäran besvaras med samma svarsrubriker som motsvarande GET-begäran; i så fall kan det returnera ett cachelagrat GET-svar för HEAD-begäran.
Dessutom ställer UpdateCacheMiddleware
automatiskt in några rubriker i varje HttpResponse`
som påverkar :ref:downstream cacher <downstream-caches>
:
Ställer in rubriken
Expires
till aktuellt datum/tid plus den definierade :inställningen:`CACHE_MIDDLEWARE_SECONDS`.Ställer in
Cache-Control
-rubriken för att ge en maxålder för sidan – återigen frånCACHE_MIDDLEWARE_SECONDS
-inställningen.
Se Middleware för mer information om middleware.
Om en vy ställer in sin egen utgångstid för cachen (dvs. den har en max-age
-sektion i sin Cache-Control
-rubrik) kommer sidan att cachelagras fram till utgångstiden, i stället för att CACHE_MIDDLEWARE_SECONDS
. Med hjälp av dekoratorerna i django.views.decorators.cache
kan du enkelt ställa in en vys utgångstid (med hjälp av dekoratorn cache_control()
) eller inaktivera cachning för en vy (med hjälp av dekoratorn never_cache()
). Se avsnittet Använda andra rubriker för mer information om dessa dekoratorer.
Om USE_I18N
är inställd på True
kommer den genererade cache-nyckeln att innehålla namnet på den aktiva language – se även Hur Django upptäcker språkpreferenser). Detta gör att du enkelt kan cachelagra flerspråkiga webbplatser utan att behöva skapa cache-nyckeln själv.
Cache-nycklar inkluderar även aktuell tidszon när USE_TZ
är satt till True
.
Cache för varje visning¶
- django.views.decorators.cache.cache_page(timeout, *, cache=None, key_prefix=None)¶
Ett mer detaljerat sätt att använda cachelagringsramverket är genom att cachelagra utdata från enskilda vyer. django.views.decorators.cache
definierar en cache_page
dekorator som automatiskt kommer att cacha vyns svar åt dig:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request): ...
cache_page
tar ett enda argument: cachens timeout, i sekunder. I exemplet ovan kommer resultatet av vyn my_view()
att cachas i 15 minuter. (Observera att vi har skrivit det som 60 * 15
för läsbarhetens skull. 60 * 15
kommer att utvärderas till 900
- det vill säga 15 minuter multiplicerat med 60 sekunder per minut)
Den timeout för cachen som ställs in av cache_page
har företräde framför max-age
-direktivet från Cache-Control
-headern.
Cachen per vy är, precis som cachen per webbplats, kopplad till webbadressen. Om flera webbadresser pekar på samma vy kommer varje webbadress att cachas separat. Om vi fortsätter med exemplet my_view
, om din URLconf ser ut så här:
urlpatterns = [
path("foo/<int:code>/", my_view),
]
så kommer förfrågningar till /foo/1/
och /foo/23/
att cachelagras separat, som du kanske förväntar dig. Men när en viss URL (t.ex. /foo/23/
) har begärts, kommer efterföljande begäranden till den URL:en att använda cacheminnet.
cache_page
kan också ta ett valfritt nyckelordsargument, cache
, som instruerar dekoratorn att använda en specifik cache (från din CACHES
-inställning) när du cachar vyresultat. Som standard kommer default
-cachen att användas, men du kan ange vilken cache du vill:
@cache_page(60 * 15, cache="special_cache")
def my_view(request): ...
Du kan också åsidosätta cacheprefixet för varje vy. cache_page
tar ett valfritt nyckelordsargument, key_prefix
, som fungerar på samma sätt som CACHE_MIDDLEWARE_KEY_PREFIX
-inställningen för mellanvaran. Den kan användas så här:
@cache_page(60 * 15, key_prefix="site1")
def my_view(request): ...
Argumenten key_prefix
och cache
kan anges tillsammans. Argumentet key_prefix
och KEY_PREFIX
som anges under CACHES
kommer att sammanfogas.
Dessutom ställer cache_page
automatiskt in Cache-Control
och Expires
rubriker i svaret som påverkar downstream caches.
Ange cache per vy i URLconf¶
Exemplen i föregående avsnitt har hårdkodat det faktum att vyn är cachad, eftersom cache_page
ändrar my_view
-funktionen på plats. Det här tillvägagångssättet kopplar din vy till cachesystemet, vilket inte är idealiskt av flera skäl. Du kanske till exempel vill återanvända vyfunktionerna på en annan webbplats utan cache, eller så kanske du vill distribuera vyerna till personer som kanske vill använda dem utan att de cachelagras. Lösningen på dessa problem är att ange cacheminnet för varje vy i URLconf i stället för bredvid själva vyfunktionerna.
Du kan göra det genom att linda in vyfunktionen med cache_page
när du hänvisar till den i URLconf. Här är den gamla URLconf från tidigare:
urlpatterns = [
path("foo/<int:code>/", my_view),
]
Här är samma sak, med my_view
inlindad i cache_page
:
from django.views.decorators.cache import cache_page
urlpatterns = [
path("foo/<int:code>/", cache_page(60 * 15)(my_view)),
]
Cachelagring av mallfragment¶
Om du vill ha ännu mer kontroll kan du också cacha mallfragment med hjälp av malltaggen cache
. För att ge din mall tillgång till denna tagg, lägg {% load cache %}
nära toppen av din mall.
Malltaggen {% cache %}
cachar innehållet i blocket under en viss tid. Den tar minst två argument: cache timeout, i sekunder, och det namn som ska ges till cachefragmentet. Fragmentet cachelagras för evigt om timeout är None
. Namnet kommer att tas som det är, använd inte en variabel. Till exempel:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Ibland kanske du vill cacha flera kopior av ett fragment beroende på dynamiska data som visas i fragmentet. Du kanske till exempel vill ha en separat cachad kopia av sidofältet som användes i föregående exempel för varje användare av din webbplats. Gör detta genom att skicka ett eller flera ytterligare argument, som kan vara variabler med eller utan filter, till malltaggen {% cache %}
för att unikt identifiera cachefragmentet:
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged-in user ..
{% endcache %}
Om USE_I18N
är inställd på True
kommer den webbplatsspecifika middleware-cachen att respektera det aktiva språket. För malltaggen cache
kan du använda en av de :ref:``översättningsspecifika variabler<template-translation-vars>` som finns i mallar för att uppnå samma resultat:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% translate "Welcome to example.com" %}
{% endcache %}
Cachens timeout kan vara en mallvariabel, så länge mallvariabeln motsvarar ett heltal. Om mallvariabeln my_timeout
till exempel är inställd på värdet 600
, är följande två exempel likvärdiga:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
Den här funktionen är användbar för att undvika upprepningar i mallar. Du kan ställa in timeouten i en variabel på ett ställe och återanvända det värdet.
Som standard kommer cache-taggen att försöka använda den cache som heter ”template_fragments”. Om ingen sådan cache finns kommer den att återgå till att använda standardcachen. Du kan välja en alternativ cache-backend att använda med nyckelordsargumentet using
, som måste vara det sista argumentet till taggen.
{% cache 300 local-thing ... using="localcache" %}
Det betraktas som ett fel att ange ett cacheminnesnamn som inte är konfigurerat.
- django.core.cache.utils.make_template_fragment_key(fragment_name, vary_on=None)¶
Om du vill få fram den cache-nyckel som används för ett cachat fragment kan du använda make_template_fragment_key
. fragment_name
är samma som det andra argumentet till malltaggen cache
; vary_on
är en lista över alla ytterligare argument som skickas till taggen. Denna funktion kan vara användbar för att t.ex. ogiltigförklara eller skriva över ett cachat objekt:
>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
>>> key = make_template_fragment_key("sidebar", [username])
>>> cache.delete(key) # invalidates cached template fragment
True
API för cache på låg nivå¶
Ibland tjänar man inte mycket på att cacha en hel renderad sida och det är i själva verket en obekväm överdrift.
Kanske innehåller din webbplats till exempel en vy vars resultat beror på flera dyra frågor, vars resultat ändras med olika intervall. I det här fallet skulle det inte vara idealiskt att använda den helsidescachelagring som cache-strategierna per webbplats eller per vy erbjuder, eftersom du inte skulle vilja cacha hela resultatet (eftersom vissa data ändras ofta), men du skulle fortfarande vilja cacha de resultat som sällan ändras.
För fall som detta exponerar Django ett API för cache på låg nivå. Du kan använda detta API för att lagra objekt i cacheminnet med vilken granularitetsnivå du vill. Du kan cachelagra alla Python-objekt som kan picklas på ett säkert sätt: strängar, ordböcker, listor med modellobjekt och så vidare. (De flesta vanliga Python-objekt kan picklas; se Python-dokumentationen för mer information om pickling)
Tillgång till cachen¶
- django.core.cache.caches¶
Du kan komma åt de cacher som konfigurerats i inställningen
CACHES
genom ett diktliknande objekt:django.core.cache.caches
. Upprepade förfrågningar om samma alias i samma tråd kommer att returnera samma objekt.>>> from django.core.cache import caches >>> cache1 = caches["myalias"] >>> cache2 = caches["myalias"] >>> cache1 is cache2 True
Om den angivna nyckeln inte finns kommer
InvalidCacheBackendError
att uppstå.För att ge trådsäkerhet kommer en annan instans av cache-backend att returneras för varje tråd.
- django.core.cache.cache¶
Som en genväg finns standardcachen tillgänglig som
django.core.cache.cache
:>>> from django.core.cache import cache
Detta objekt är likvärdigt med
caches['default']
.
Grundläggande användning¶
Det grundläggande gränssnittet är:
- cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)¶
>>> cache.set("my_key", "hello, world!", 30)
- cache.get(key, default=None, version=None)¶
>>> cache.get("my_key")
'hello, world!'
key
bör vara en str
och value
kan vara vilket Python-objekt som helst.
Argumentet timeout
är valfritt och standardvärdet är argumentet timeout
för lämplig backend i inställningen CACHES
(förklaras ovan). Det är antalet sekunder som värdet ska lagras i cacheminnet. Om du anger None
för timeout
kommer värdet att cachas för alltid. En timeout
på 0
kommer inte att cachelagra värdet.
Om objektet inte finns i cacheminnet returnerar cache.get()
None
:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get("my_key")
None
Om du behöver avgöra om objektet finns i cacheminnet och du har lagrat ett bokstavligt värde None
, använd ett sentinel-objekt som standard:
>>> 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
cache.get()
kan ta ett default
argument. Detta anger vilket värde som ska returneras om objektet inte finns i cacheminnet:
>>> cache.get("my_key", "has expired")
'has expired'
- cache.add(key, value, timeout=DEFAULT_TIMEOUT, version=None)¶
Om du vill lägga till en nyckel bara om den inte redan finns använder du metoden add()
. Den tar samma parametrar som set()
, men den kommer inte att försöka uppdatera cachen om den angivna nyckeln redan finns:
>>> cache.set("add_key", "Initial value")
>>> cache.add("add_key", "New value")
>>> cache.get("add_key")
'Initial value'
Om du behöver veta om add()
har lagrat ett värde i cacheminnet kan du kontrollera returvärdet. Det kommer att returnera True
om värdet lagrades, False
annars.
- cache.get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None)¶
Om du vill hämta en nyckels värde eller ange ett värde om nyckeln inte finns i cacheminnet finns metoden get_or_set()
. Den tar samma parametrar som get()
men standardvärdet sätts som det nya cachervärdet för den nyckeln, istället för att returneras:
>>> cache.get("my_new_key") # returns None
>>> cache.get_or_set("my_new_key", "my new value", 100)
'my new value'
Du kan också skicka vilken callable som helst som ett standardvärde:
>>> import datetime
>>> cache.get_or_set("some-timestamp-key", datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
- cache.get_many(keys, version=None)¶
Det finns också ett get_many()
-gränssnitt som bara träffar cacheminnet en gång. get_many()
returnerar en ordbok med alla nycklar du bad om som faktiskt finns i cachen (och som inte har gått ut):
>>> cache.set("a", 1)
>>> cache.set("b", 2)
>>> cache.set("c", 3)
>>> cache.get_many(["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
- cache.set_many(dict, timeout)¶
Om du vill ange flera värden mer effektivt kan du använda set_many()
för att skicka en ordbok med nyckel-värdepar:
>>> cache.set_many({"a": 1, "b": 2, "c": 3})
>>> cache.get_many(["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
Liksom cache.set()
har set_many()
en valfri parameter timeout
.
På backends som stöds (memcached) returnerar set_many()
en lista över nycklar som inte kunde infogas.
- cache.delete(key, version=None)¶
Du kan ta bort nycklar explicit med delete()
för att rensa cacheminnet för ett visst objekt:
>>> cache.delete("a")
True
delete()
returnerar True
om nyckeln har raderats framgångsrikt, False
annars.
- cache.delete_many(keys, version=None)¶
Om du vill rensa en massa nycklar på en gång kan delete_many()
ta emot en lista med nycklar som ska rensas:
>>> cache.delete_many(["a", "b", "c"])
- cache.clear()¶
Slutligen, om du vill ta bort alla nycklar i cacheminnet, använd cache.clear()
. Var försiktig med detta; clear()
kommer att ta bort allt från cachen, inte bara de nycklar som ställts in av din applikation:
>>> cache.clear()
- cache.touch(key, timeout=DEFAULT_TIMEOUT, version=None)¶
cache.touch()
anger en ny utgångsdatum för en nyckel. Till exempel, för att uppdatera en nyckel så att den upphör att gälla om 10 sekunder:
>>> cache.touch("a", 10)
True
Precis som för andra metoder är argumentet timeout
valfritt och standardvärdet är alternativet TIMEOUT
för lämplig backend i inställningen CACHES
.
touch()
returnerar True
om nyckeln berördes framgångsrikt, False
annars.
- cache.incr(key, delta=1, version=None)¶
- cache.decr(key, delta=1, version=None)¶
Du kan också öka eller minska en nyckel som redan finns med hjälp av metoderna incr()
respektive decr()
. Som standard kommer det befintliga cache-värdet att ökas eller minskas med 1. Andra öknings-/dekrementeringsvärden kan anges genom att tillhandahålla ett argument till increment/decrement-anropet. Ett ValueError kommer att uppstå om du försöker öka eller minska en icke-existerande cache-nyckel:
>>> cache.set("num", 1)
>>> cache.incr("num")
2
>>> cache.incr("num", 10)
12
>>> cache.decr("num")
11
>>> cache.decr("num", 5)
6
Observera
metoderna incr()
/decr()` garanteras inte att vara atomiska. På de backends som stödjer atomär inkrementering/dekrementering (framför allt memcached-backend) kommer inkrementerings- och dekrementeringsoperationer att vara atomära. Men om backend inte naturligt tillhandahåller en inkrementerings-/dekrementeringsoperation kommer den att implementeras med hjälp av en tvåstegshämtning/uppdatering.
- cache.close()¶
Du kan stänga anslutningen till din cache med close()
om det implementeras av cache-backend.
>>> cache.close()
Observera
För cacher som inte implementerar close
-metoder är det ett no-op.
Observera
De asynkrona varianterna av basmetoderna har prefixet a
, t.ex. cache.aadd()
eller cache.adelete_many()
. Se Asynkront stöd för mer information.
Prefixering av nyckel i cacheminnet¶
Om du delar en cache-instans mellan olika servrar, eller mellan produktions- och utvecklingsmiljöerna, är det möjligt att data som har cachats av en server används av en annan server. Om formatet för cachade data skiljer sig åt mellan servrarna kan det leda till problem som är mycket svåra att diagnostisera.
För att förhindra detta ger Django möjlighet att prefixa alla cache-nycklar som används av en server. När en viss cache-nyckel sparas eller hämtas kommer Django automatiskt att prefixa cache-nyckeln med värdet av cache-inställningen KEY_PREFIX
.
Genom att se till att varje Django-instans har en annan KEY_PREFIX
kan du se till att det inte blir några kollisioner i cache-värden.
Versionering av cachen¶
När du ändrar körkod som använder cachade värden kan du behöva rensa bort eventuella befintliga cachade värden. Det enklaste sättet att göra detta är att rensa hela cacheminnet, men det kan leda till att cacheminnevärden som fortfarande är giltiga och användbara går förlorade.
Django tillhandahåller ett bättre sätt att rikta in sig på enskilda cache-värden. Djangos cache-ramverk har en systemomfattande versionsidentifierare, som anges med hjälp av cache-inställningen VERSION
. Värdet på denna inställning kombineras automatiskt med cacheprefixet och den användartillhandahållna cachekoden för att erhålla den slutliga cachekoden.
Som standard kommer alla nyckelbegäran automatiskt att inkludera webbplatsens standardversion av cache-nyckeln. De primitiva cachefunktionerna innehåller dock alla ett version
-argument, så att du kan ange en viss cache-nyckelversion för att ställa in eller hämta. Till exempel:
>>> # Set version 2 of a cache key
>>> cache.set("my_key", "hello world!", version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get("my_key")
None
>>> # Get version 2 of the same key
>>> cache.get("my_key", version=2)
'hello world!'
Versionen av en specifik nyckel kan ökas och minskas med hjälp av metoderna incr_version()
och decr_version()
. Detta gör att specifika nycklar kan flyttas till en ny version, medan andra nycklar inte påverkas. Fortsättning på vårt tidigare exempel:
>>> # Increment the version of 'my_key'
>>> cache.incr_version("my_key")
>>> # The default version still isn't available
>>> cache.get("my_key")
None
# Version 2 isn't available, either
>>> cache.get("my_key", version=2)
None
>>> # But version 3 *is* available
>>> cache.get("my_key", version=3)
'hello world!'
Transformation av cache-nyckel¶
Som beskrivs i de två föregående avsnitten används inte den cachekod som en användare anger ordagrant, utan den kombineras med cacheprefixet och nyckelversionen för att ge en slutlig cachekod. Som standard sammanfogas de tre delarna med hjälp av kolon för att skapa en slutlig sträng:
def make_key(key, key_prefix, version):
return "%s:%s:%s" % (key_prefix, version, key)
Om du vill kombinera delarna på olika sätt eller tillämpa annan bearbetning på den slutliga nyckeln (t.ex. ta en hashdigest av nyckeldelarna) kan du tillhandahålla en anpassad nyckelfunktion.
Cacheinställningen KEY_FUNCTION
anger en prickad sökväg till en funktion som motsvarar prototypen för make_key()
ovan. Om den anges kommer denna anpassade nyckelfunktion att användas i stället för standardfunktionen för nyckelkombination.
Varningar för cache-nycklar¶
Memcached, den mest använda produktionscache-backend, tillåter inte cache-nycklar som är längre än 250 tecken eller innehåller blanksteg eller kontrolltecken, och användning av sådana nycklar kommer att orsaka ett undantag. För att uppmuntra cache-portabel kod och minimera obehagliga överraskningar utfärdar de andra inbyggda cache-backends en varning (django.core.cache.backends.base.CacheKeyWarning
) om en nyckel används som skulle orsaka ett fel på memcached.
Om du använder en produktionsbackend som kan acceptera ett bredare utbud av nycklar (en anpassad backend eller en av de inbyggda backend som inte är memcached) och vill använda detta bredare utbud utan varningar, kan du tysta CacheKeyWarning
med den här koden i modulen management
i en av dina INSTALLED_APPS
:
import warnings
from django.core.cache import CacheKeyWarning
warnings.simplefilter("ignore", CacheKeyWarning)
Om du istället vill tillhandahålla anpassad logik för nyckelvalidering för en av de inbyggda backends kan du underklassa den, åsidosätta bara metoden validate_key
och följa instruktionerna för använda en anpassad cache-backend. Till exempel, för att göra detta för locmem
backend, lägg den här koden i en modul:
from django.core.cache.backends.locmem import LocMemCache
class CustomLocMemCache(LocMemCache):
def validate_key(self, key):
"""Custom validation, raising exceptions or warnings as needed."""
...
…och använd den prickade Python-sökvägen till den här klassen i BACKEND
-delen av din CACHES
-inställning.
Asynkront stöd¶
Django har utvecklat stöd för asynkrona cache-backends, men har ännu inte stöd för asynkron cachelagring. Det kommer att komma i en framtida version.
django.core.cache.backends.base.BaseCache
har asynkrona varianter av :ref:alla basmetoder <cache-basic-interface>
. Av konvention är de asynkrona versionerna av alla metoder prefixerade med a
. Som standard är argumenten för båda varianterna desamma:
>>> await cache.aset("num", 1)
>>> await cache.ahas_key("num")
True
Cacher nedströms¶
Hittills har detta dokument fokuserat på cachelagring av dina egna data. Men en annan typ av cachelagring är också relevant för webbutveckling: cachelagring som utförs av ”downstream”-cacher. Det är system som cachar sidor för användare redan innan förfrågan når din webbplats.
Här är några exempel på downstream-cacher:
När du använder HTTP kan din ISP cachelagra vissa sidor, så om du begärde en sida från
http://example.com/
skulle din ISP skicka sidan till dig utan att behöva gå direkt till example.com. Underhållarna av example.com har ingen kännedom om denna cachelagring; ISP:n sitter mellan example.com och din webbläsare och hanterar all cachelagring på ett transparent sätt. Sådan cachelagring är inte möjlig under HTTPS eftersom det skulle utgöra en man-in-the-middle-attack.Din Django-webbplats kan ligga bakom en proxy-cache, till exempel Squid Web Proxy Cache (http://www.squid-cache.org/), som cachelagrar sidor för prestanda. I det här fallet skulle varje begäran först hanteras av proxyn, och den skulle skickas till din applikation endast om det behövs.
Även din webbläsare cachar sidor. Om en webbsida skickar ut lämpliga rubriker kommer din webbläsare att använda den lokala cachade kopian för efterföljande förfrågningar till den sidan, utan att ens kontakta webbsidan igen för att se om den har ändrats.
Cachelagring nedströms är en trevlig effektivitetshöjare, men det finns en fara med det: Innehållet på många webbsidor skiljer sig åt beroende på autentisering och en mängd andra variabler, och cachesystem som blint sparar sidor baserat enbart på webbadresser kan exponera felaktiga eller känsliga data för efterföljande besökare på dessa sidor.
Om du t.ex. driver ett webbaserat e-postsystem beror innehållet på sidan ”inkorg” på vilken användare som är inloggad. Om en internetleverantör cachelagrade din webbplats i blindo skulle den första användaren som loggade in via den internetleverantören få sin användarspecifika inkorgssida cachelagrad för efterföljande besökare på webbplatsen. Det är inte coolt.
Lyckligtvis erbjuder HTTP en lösning på detta problem. Det finns ett antal HTTP-rubriker som instruerar nedströmscacher att variera innehållet i cacheminnet beroende på angivna variabler och som talar om för cachemekanismer att de inte ska cacha vissa sidor. Vi kommer att titta på några av dessa rubriker i de avsnitt som följer.
Använda Vary
-rubriker¶
Headern Vary
definierar vilka förfrågningsheaders som en cachemekanism ska ta hänsyn till när den bygger upp sin cache-nyckel. Om t.ex. innehållet på en webbsida beror på användarens språkpreferens, sägs sidan ”variera på språk”
Som standard skapar Djangos cachesystem sina cache-nycklar med hjälp av den begärda fullständigt kvalificerade URL:en - t.ex. "https://www.example.com/stories/2005/?order_by=author"
. Detta innebär att varje begäran till den webbadressen kommer att använda samma cachade version, oavsett skillnader i användaragenter som cookies eller språkpreferenser. Men om den här sidan producerar olika innehåll baserat på någon skillnad i förfrågningsrubrikerna - till exempel en cookie, ett språk eller en användaragent - måste du använda Vary
-rubriken för att berätta för cachemekanismerna att sidans utdata beror på dessa saker.
För att göra detta i Django, använd den praktiska django.views.decorators.vary.vary_on_headers()
vydekorator, så här:
from django.views.decorators.vary import vary_on_headers
@vary_on_headers("User-Agent")
def my_view(request): ...
I det här fallet kommer en cachemekanism (t.ex. Djangos egen cachemellanvara) att cacha en separat version av sidan för varje unik användaragent.
Fördelen med att använda dekoratorn vary_on_headers
i stället för att manuellt ställa in rubriken Vary
(med något som response.headers['Vary'] = 'user-agent'
) är att dekoratorn lägger till rubriken Vary
(som kanske redan finns), i stället för att ställa in den från början och potentiellt åsidosätta något som redan fanns där.
Du kan skicka flera rubriker till vary_on_headers()
:
@vary_on_headers("User-Agent", "Cookie")
def my_view(request): ...
Detta innebär att nedströmscacher varierar på båda, vilket innebär att varje kombination av user-agent och cookie får sitt eget cachervärde. Till exempel kommer en begäran med user-agent Mozilla
och cookie-värdet foo=bar
att betraktas annorlunda än en begäran med user-agent Mozilla
och cookie-värdet foo=ham
.
Eftersom det är så vanligt att variera på cookie finns det en django.views.decorators.vary.vary_on_cookie()
dekorator. Dessa två vyer är likvärdiga:
@vary_on_cookie
def my_view(request): ...
@vary_on_headers("Cookie")
def my_view(request): ...
De rubriker som du skickar till vary_on_headers
är inte skiftlägeskänsliga; "User-Agent"
är samma sak som "user-agent"
.
Du kan också använda en hjälpfunktion, django.utils.cache.patch_vary_headers()
, direkt. Denna funktion ställer in, eller lägger till, Vary header
. Till exempel:
from django.shortcuts import render
from django.utils.cache import patch_vary_headers
def my_view(request):
...
response = render(request, "template_name", context)
patch_vary_headers(response, ["Cookie"])
return response
patch_vary_headers
tar en HttpResponse
-instans som sitt första argument och en lista/tupel av skiftlägesokänsliga headernamn som sitt andra argument.
Mer information om Vary-rubriker finns i officiella Vary-specifikationer.
Kontroll av cache: Använda andra rubriker¶
Andra problem med cachelagring är integriteten för data och frågan om var data ska lagras i en kaskad av cacher.
En användare har vanligtvis två typer av cacheminnen: den egna webbläsarens cacheminne (ett privat cacheminne) och leverantörens cacheminne (ett publikt cacheminne). En publik cache används av flera användare och kontrolleras av någon annan. Detta innebär problem med känsliga data - du vill till exempel inte att ditt bankkontonummer ska lagras i en publik cache. Webbapplikationer behöver därför ett sätt att tala om för cacher vilka data som är privata och vilka som är offentliga.
Lösningen är att ange att en sidas cache ska vara ”privat” För att göra detta i Django, använd cache_control()
vydekorator. Exempel:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request): ...
Denna dekoratör tar hand om att skicka ut lämplig HTTP-header bakom kulisserna.
Observera att cachekontrollinställningarna ”private” och ”public” är ömsesidigt uteslutande. Dekoratorn säkerställer att ”public”-direktivet tas bort om ”private” ska ställas in (och vice versa). Ett exempel på användning av de två direktiven är en blogg som erbjuder både privata och offentliga inlägg. Offentliga poster kan cachelagras på vilken delad cache som helst. Följande kod använder patch_cache_control()
, det manuella sättet att ändra cache-kontrollhuvudet (det kallas internt av cache_control()
-dekoratorn):
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie
@vary_on_cookie
def list_blog_entries_view(request):
if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
response = render_private_and_public_entries(request.user)
patch_cache_control(response, private=True)
return response
Du kan kontrollera nedströmscacher på andra sätt också (se RFC 9111 för detaljer om HTTP-cachning). Till exempel, även om du inte använder Djangos cache-ramverk på serversidan, kan du fortfarande berätta för klienter att cacha en vy under en viss tid med max-age-direktivet:
from django.views.decorators.cache import cache_control
@cache_control(max_age=3600)
def my_view(request): ...
(Om du använd mellanvaran för cachelagring ställer den redan in max-age
med värdet för inställningen CACHE_MIDDLEWARE_SECONDS
. I så fall kommer den anpassade max_age
från cache_control()
-dekoratorn att ha företräde, och rubrikvärdena kommer att slås samman korrekt)
Alla giltiga svarsdirektiv för Cache-Control
är giltiga i cache_control()
. Här är några fler exempel:
no_transform=True
must_revalidate=True
stale_while_revalidate=num_seconds
no_cache=True
Den fullständiga listan över kända direktiv finns i ”IANA registry” (observera att inte alla direktiv gäller för svar).
Om du vill använda rubriker för att inaktivera cachning helt och hållet är never_cache()
en vydekorator som lägger till rubriker för att säkerställa att svaret inte kommer att cachas av webbläsare eller andra cachar. Exempel:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request): ...
Beställning av MIDDLEWARE
¶
Om du använder mellanprogram för cache är det viktigt att du placerar varje halva på rätt plats i MIDDLEWARE
-inställningen. Det beror på att cachemellanvaran måste veta vilka rubriker som ska användas för att variera cachelagringen. Middleware lägger alltid till något i svarshuvudet Vary
när det kan.
UpdateCacheMiddleware
körs under svarsfasen, där middleware körs i omvänd ordning, så ett objekt högst upp i listan körs sist under svarsfasen. Därför måste du se till att UpdateCacheMiddleware
visas före alla andra mellanprogram som kan lägga till något i Vary
-headern. Följande middleware-moduler gör detta:
SessionMiddleware
lägger tillCookie
GZipMiddleware
lägger tillAccept-Encoding
LocaleMiddleware
lägger tillAccept-Language
FetchFromCacheMiddleware
körs å andra sidan under request-fasen, där middleware tillämpas först till sist, så ett objekt högst upp i listan körs först under request-fasen. FetchFromCacheMiddleware
måste också köras efter att annan middleware uppdaterar Vary
-huvudet, så FetchFromCacheMiddleware
måste vara efter alla objekt som gör det.