Ho utilizzato lo strumento UpdateCacheMiddleware
e FetchFromCacheMiddleware
MiddleWare per abilitare il caching anonimo del sito a vari livelli di successo.Tecnica per sottoclassi di Django UpdateCacheMiddleware e FetchFromCacheMiddleware
Il problema più grande è che il middleware memorizza nella cache solo la prima richiesta di un utente anonimo. Poiché un cookie session_id è impostato su quella prima risposta, le richieste successive da parte di quell'utente anonimo non colpiscono la cache come risultato del fatto che la cache a livello di vista variava sulle intestazioni.
Le mie pagine Web non variano in modo significativo tra gli utenti anonimi e, nella misura in cui variano, posso gestirli tramite Ajax. Di conseguenza, ho deciso di provare a sottoclasse il middleware di cache di Django per non modificare più l'intestazione. Al contrario, varia su Anonymous vs LoggedIn Users. Perché sto usando il backend di Auth, e quel gestore si verifica prima di prelevare dalla cache, sembra funzionare.
class AnonymousUpdateCacheMiddleware(UpdateCacheMiddleware):
def process_response(self, request, response):
"""
Sets the cache, if needed.
We are overriding it in order to change the behavior of learn_cache_key().
"""
if not self._should_update_cache(request, response):
# We don't need to update the cache, just return.
return response
if not response.status_code == 200:
return response
timeout = get_max_age(response)
if timeout == None:
timeout = self.cache_timeout
elif timeout == 0:
# max-age was set to 0, don't bother caching.
return response
patch_response_headers(response, timeout)
if timeout:
######### HERE IS WHERE IT REALLY GOES DOWN #######
cache_key = self.learn_cache_key(request, response, self.cache_timeout, self.key_prefix, cache=self.cache)
if hasattr(response, 'render') and callable(response.render):
response.add_post_render_callback(
lambda r: self.cache.set(cache_key, r, timeout)
)
else:
self.cache.set(cache_key, response, timeout)
return response
def learn_cache_key(self, request, response, timeout, key_prefix, cache=None):
"""_generate_cache_header_key() creates a key for the given request path, adjusted for locales.
With this key, a new cache key is set via _generate_cache_key() for the HttpResponse
The subsequent anonymous request to this path hits the FetchFromCacheMiddleware in the
request capturing phase, which then looks up the headerlist value cached here on the initial response.
FetchFromMiddleWare calcuates a cache_key based on the values of the listed headers using _generate_cache_key
and then looks for the response stored under that key. If the headers are the same as those
set here, there will be a cache hit and the cached HTTPResponse is returned.
"""
key_prefix = key_prefix or settings.CACHE_MIDDLEWARE_KEY_PREFIX
cache_timeout = self.cache_timeout or settings.CACHE_MIDDLEWARE_SECONDS
cache = cache or get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
cache_key = _generate_cache_header_key(key_prefix, request)
# Django normally varies caching by headers so that authed/anonymous users do not see same pages
# This makes Google Analytics cookies break caching;
# It also means that different anonymous session_ids break caching, so only first anon request works
# In this subclass, we are ignoring headers and instead varying on authed vs. anonymous users
# Alternatively, we could also strip cookies potentially for the same outcome
# if response.has_header('Vary'):
# headerlist = ['HTTP_' + header.upper().replace('-', '_')
# for header in cc_delim_re.split(response['Vary'])]
# else:
headerlist = []
cache.set(cache_key, headerlist, cache_timeout)
return _generate_cache_key(request, request.method, headerlist, key_prefix)
Il Fetcher, che è responsabile per il recupero della pagina dalla cache, si presenta così
class AnonymousFetchFromCacheMiddleware(FetchFromCacheMiddleware):
def process_request(self, request):
"""
Checks whether the page is already cached and returns the cached
version if available.
"""
if request.user.is_authenticated():
request._cache_update_cache = False
return None
else:
return super(SmarterFetchFromCacheMiddleware, self).process_request(request)
C'era un sacco di copia per UpdateCacheMiddleware
, ovviamente. Non riuscivo a capire un gancio migliore per rendere questo più pulito.
Questo sembra generalmente un buon approccio? Qualche problema ovvio che ti viene in mente?
Grazie, Ben
Ciao okm, la mia implementazione lo fa efficacemente, giusto? – Ben
@ Ben Non c'è grande differenza. Ma confrontando il codice con la duplicazione, preferisco l'ereditarietà qui. È più chiaro, ha meno codice nel tuo progetto e causerebbe meno mal di testa quando aggiorni la versione di Django – okm
grazie a okm, tutti i punti positivi – Ben