Timestamp für statische Mediafiles
Statische Dateien, etwa CSS und JavaScript Dateien sollte man möglichst mit einem langen Expire-Header ausliefern, damit die Clients (Browser) diese nicht ständig neu vom Server laden. Benutzt man ETags wird bei jedem Zugriff eine Abfrage an den Server gesendet und ggf. mit einem 304er (Not Modified) beantwortet, mit einem vernünftigen Expire-Header wird diese Abfrage gespart.
Versieht man nun alle CSS und JS Dateien mit einem Expire-Header der weit in der Zukunft ausläuft, so ist das Problem, dass man jedesmal den Dateinamen ändern muss, wenn sich der Inhalt geändert hat, da sonst der Browser nie die neue Datei laden wird.
Der folgende Templatetag soll dieses Problem umgehen, in dem ein Timestamp der letzten Modifikation in den Dateinamen eingebaut wird, welcher dann per mod_rewrite (Apache) wieder entfernt wird, bevor die Datei vom Dateisystem gelesen wird.
Der Timestamp kommt in den Dateinamen und nicht einfach als Querystring hinten dran, da es dann komplizierter wird sobald ein Proxy zwischen Server und Client geschaltet ist. Zur Erklärung siehe auch: http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
Diese Methode soll das Caching von Mediafiles im Zusammenhang mit Reverse-Proxys for der eigentlichen Applikation erheblich vereinfachen.
import os
from django import template
from django.conf import settings
register = template.Library()
def expire_tag(filename):
"""
Add a timestamp of the last modification to files embedded with this
tag. The timestamp should be stripped with mod_rewrite or something similar.
This enables us to set very long expire headers on media files while editing
the file will automatically invalidate the old file and lets browsers fetch
the new one. Embedding the timestamp in the filename is safer than just
appending a QueryString. See:
http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
Written by: Arne Brodowski http://www.arnebrodowski.de/
Usage:
{% expire_tag "css/reset.css" %}
will output (where 1234567890 is a timestamp of the last modification):
css/reset.123456890.css
Example usage in a template:
<link rel="stylesheet" href="{{ MEDIA_URL }}{% expire_tag "css/reset.css" %}" type="text/css" />
Example mod_rewrite code for usage in .htaccess file::
RewriteEngine On
RewriteRule ^(.*)\.[0-9]+\.(css|js)$ /$1.$2 [L]
Example nginx rewrite code (must be outside of location block)::
rewrite ^(.*)\.[0-9]+\.(css|js)$ $1.$2 last;
"""
if not settings.DEBUG:
try:
mtime = os.path.getmtime(os.path.join(settings.STATIC_ROOT, filename))
parts = filename.rsplit('.', 1)
return "%s.%s.%s" % (parts[0], int(mtime), parts[1])
except:
return filename
else:
return filename
register.simple_tag(expire_tag)
Ein anderer Ansatz, der nur einen Hash als Querystring an die Mediafiles anhängt ist hier zu finden: http://www.20seven.org/journal/2008/09/django-cachebuster.html
Der Sinn dieser Methode ist es die Dateien möglichst unbegrenzt cachen zu können, sowohl am Client als auch evtl. in einem Proxy-Server und trotzdem in der Lage zu sein die Caches bei einer Änderung invalidieren zu können. Das ganze dann noch ohne, dass der Entwickler oder Admin dran denken muss einen Dateinamen zu ändern oder irgendein Skript auszuführen.



