Kampf gegen clientseitiges Caching in Django

69

Ich verwende die Verknüpfung render_to_response und möchte kein bestimmtes Antwortobjekt erstellen, um zusätzliche Header hinzuzufügen, um das clientseitige Caching zu verhindern.

Ich hätte gerne eine Antwort, die enthält:

  • Pragma: kein Cache
  • Cache-Kontrolle: kein Cache
  • Cache-Kontrolle: muss erneut validiert werden

Und all die anderen raffinierten Möglichkeiten, die Browser hoffentlich als Anweisungen interpretieren, um das Caching zu vermeiden.

Gibt es eine No-Cache-Middleware oder ähnliches, die den Trick mit minimalem Codeeinbruch ausführen kann?

Lorenzo
quelle

Antworten:

96

Sie können dies mit dem Dekorator cache_control erreichen. Beispiel aus der Dokumentation :

from django.views.decorators.cache import never_cache

@never_cache
def myview(request):
   # ...
Kristian
quelle
16
Um diese Arbeit auf allen Browsern (insbesondere FireFox und Opera, es funktionierte fein auf IE und Safari / Chrome) Ich musste manuell hinzufügen response["Cache-Control"] = "no-cache, no-store, must-revalidate"zusammen mit @never_cache. @never_cacheAnrufe add_never_cache_headers()und dies wiederum ruft, patch_cache_control()aber dies fügt nur hinzu Cache-Control:max-age=0, was anscheinend für diese Browser nicht ausreicht. Siehe stackoverflow.com/questions/49547/…
AJJ
9
Nachdem ich den Django-Code etwas genauer untersucht hatte, fand ich eine sauberere Möglichkeit, diesen Header hinzuzufügen: patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True)
AJJ
6
Ah, es gibt bereits ein offenes Ticket dafür unter code.djangoproject.com: @never_cache decorator sollte 'no-cache' & 'must-
revalidate
1
@AJJ Ich denke, Sie haben auch verpasstresponse['Pragma'] = 'no-cache'
Ory Band
7
Update im Jahr 2018: @never_cachewurde behoben, um auf allen Browsern zu funktionieren.
Mathew
51

Dieser Ansatz (geringfügige Änderung der Lösung von L. De Leo) mit einer benutzerdefinierten Middleware hat sich für mich als standortweite Lösung bewährt:

from django.utils.cache import add_never_cache_headers

class DisableClientSideCachingMiddleware(object):
    def process_response(self, request, response):
        add_never_cache_headers(response)
        return response

Dies macht Gebrauch von add_never_cache_headers.


Wenn Sie dies mit UpdateCacheMiddlewareund kombinieren möchten FetchFromCacheMiddleware, um das serverseitige Caching zu aktivieren und gleichzeitig das clientseitige Caching zu deaktivieren, müssen Sie DisableClientSideCachingMiddlewarevor allem anderen Folgendes hinzufügen :

MIDDLEWARE_CLASSES = (
    'custom.middleware.DisableClientSideCachingMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    # ... all other middleware ...
    'django.middleware.cache.FetchFromCacheMiddleware',
)
Meilo
quelle
5
+1 für die Verwendung add_never_cache_headersanstelle des manuellen Einfügens von Headern.
Michael Mior
Ich habe etwas basierend darauf verpackt. Es ist jetzt auf PyPI und Github verfügbar. github.com/incuna/django-never-cache-post
meshy
Nur zur Info: Dies funktioniert nicht wirklich für Opera und das Zwischenspeichern von Seiten, da add_never_cache nur das maximale Alter auf Null setzt und O, und Opera das maximale Alter für diesen Zweck nicht zu berücksichtigen scheint. Siehe my.opera.com/yngve/blog/2007/02/27/…
AdamC
Nicht genau, @AdamC. Wenn Sie mindestens zu Django 1.5 zurückkehren, werden add_never_cache_headersauch die Header "Expires" und "Last-Modified" auf die aktuelle Zeit gesetzt. Außerdem werden ETag-Header so eingestellt, dass das Caching verhindert wird, wenn USE_ETAGS in Ihren Einstellungen festgelegt ist. Siehe github.com/django/django/blob/master/django/utils/cache.py
B Robster
Tolle Antwort. Ich habe eine Neufassung für django 1.10+ stackoverflow.com/a/47684405/2800876
Zags
16

Ergänzende Antworten ergänzen. Hier ist ein Dekorateur, der zusätzliche Header hinzufügt, um das Caching zu deaktivieren:

from django.views.decorators.cache import patch_cache_control
from functools import wraps

def never_ever_cache(decorated_function):
    """Like Django @never_cache but sets more valid cache disabling headers.

    @never_cache only sets Cache-Control:max-age=0 which is not
    enough. For example, with max-axe=0 Firefox returns cached results
    of GET calls when it is restarted.
    """
    @wraps(decorated_function)
    def wrapper(*args, **kwargs):
        response = decorated_function(*args, **kwargs)
        patch_cache_control(
            response, no_cache=True, no_store=True, must_revalidate=True,
            max_age=0)
        return response
    return wrapper

Und Sie können es verwenden wie:

class SomeView(View):
    @method_decorator(never_ever_cache)
    def get(self, request):
        return HttpResponse('Hello')
Jan Wrobel
quelle
Kann jemand die Abwahl erklären? Ich frage mich, ob etwas grundlegend mit dem Code nicht stimmt, weil ich in einem Produktionssystem darauf angewiesen bin.
Jan Wrobel
+1 Funktioniert auch für mich und ich sehe auch kein Problem. Es wäre sehr dankbar, einen Grund vom Downvoter zu hören.
Zerm
7

Eigentlich war es einfach genug, meine eigene Middleware zu schreiben:

from django.http import HttpResponse


class NoCacheMiddleware(object):

    def process_response(self, request, response):

        response['Pragma'] = 'no-cache'
        response['Cache-Control'] = 'no-cache must-revalidate proxy-revalidate'

        return response

Benimmt sich immer noch nicht so, wie ich es wollte, aber der @ never_cache-Dekorator auch nicht

Lorenzo
quelle
1
Diese Antwort auf Sicherstellen, dass eine Webseite nicht zwischengespeichert wird, ist in allen Browsern sehr detailliert: stackoverflow.com/questions/49547/…
AJJ
5

In Bezug auf den Google Chrome-Browser (Version 34.0.1847.116 m) und die anderen Browser stellte ich fest, dass nur der @cache_controlDekorateur funktioniert. Ich benutze Django 1.6.2.

Verwenden Sie es so:

@cache_control(max_age=0, no_cache=True, no_store=True, must_revalidate=True)
def view(request):
    ...
Erwan
quelle
1
Was ist der beste Weg, um dies zu tun, wenn man klassenbasierte Ansichten verwendet?
Hassan Baig
5

Hier ist eine Neufassung von @ Meilos Antwort für Django 1.10+:

from django.utils.cache import add_never_cache_headers

class DisableClientCachingMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        add_never_cache_headers(response)
        return response
Zags
quelle
5

Ich kratzte mir am Kopf, als die drei Magie metain Firefox und Safari nicht funktionierte.

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

Anscheinend kann es passieren, dass einige Browser die Clientseite ignorieren meta, daher sollte dies auf der Serverseite behandelt werden.

Ich habe alle Antworten aus diesem Beitrag für meine klassenbasierten Ansichten ( django==1.11.6) ausprobiert . Aber unter Bezugnahme auf die Antworten von @Lorenzo und @Zags habe ich beschlossen, eine Middleware zu schreiben, die ich für einfach halte.

Also zu anderen guten Antworten hinzufügen,

# middleware.py
class DisableBrowserCacheMiddleware(object):

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['Pragma'] = 'no-cache'
        response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
        response['Expires'] = '0'
        return response

# settings.py
MIDDLEWARE = [
    'myapp.middleware.DisableBrowserCacheMiddleware',
    ...
Hussain
quelle
Brillant. Dank dafür.
michela