Django ve browser cache invalidation
Dec 25, 2016 · 3 minute read · Commentsprogrammingpython
Onceki yazimda HTTP caching mekanizmasinin nasil calistigindan birazcik deginmistim. Bu yazimda ise asagidaki sorunu nasil cozdugumu anlatmaya calisacagim.
Hem statik dosyalarimizin client-side’da en uzun sure cachelenebilmesini, hemde statik dosyalarimizda degisiklik olunca clientlarin dosyanin yeni veriyonunu indirmelerini nasil saglayabiliriz?
Ben bu sorunun cevabini Django uzerinden anlatacagim ama siz hangi programlama dili/framework kullaniyorsaniz onda da ayni sekil yapabilirsiniz.
Sunucumuzda style.css
adinda bir css dosyamiz oldugu varsayalim. Biz bu dosyanin kullanicinin
tarayicisinin cachinde maximum sure durmasini, dosyamizin degismesi durumunda da tarayicinin
dosyamizin yeni versiyonunu indirmesini ve kullanmasini su sekilde sagalayabiliriz:
Sunucumuzda bulunan style.css
dosyamizi style.<dosyanin_en_son_degistirildigi_timestamp>.css
olarak kullaniciya gondererek saglayabiliriz.
Bu sayede dosya degistiginde dosyanin degistirilme tarihide dolayisiyla dosya ismi otomatikmen
degisecek ve tarayicinin cacheinde bu isimde bir dosya olmadigindan sunucuya yeni bir istek
gonderek dosyanin yeni versiyonunu indirecektir.
Simdi bunu Django ile nasil yaptigimdan bahsetmek istiyorum. Yaptigim sey cok basit.
Normalde bir css dosyasini sayfamiza su sekilde ekliyoruz:
<link href="{% static 'css/style.css' %}" .... />
CSS dosyalarini bu sekilde sayfamiza eklemek yerine custom bir template tag olusturdum(Template tagin kodunu yazinin sonunda paylastim):
{% css 'css/style.css' %}
Bu template tagi verdigim pathe dosyanin degistirilme tarihini ekliyor ve su sekil bir cikti uretiyor.
<link href="/static/css/style.min.1473070177.css" .... />
Css dosyamizin include edildigi sayfa client a gonderiliyor, client css dosyasini almak icin sunucuya tekrar istek yaptiginda nginx istegi karsiliyor ve istenilen dosyanin isminden timestampi cikarip orjinal dosyayi client a gonderiyor. Dosya her degistiginde timestamp degisecegi icin dosyalarimizin istedigimiz kadar uzun sure cache de kalmasini saglayabiliriz.
Bu sekilde hem statik dosyalarimizin isminde herhangi bir degisiklik yapmadan temiz bir sekilde cozmus oluyoruz.
Template taginin kodu (Django 1.10):
# common_tags.py
import os
import re
from django import template
from django.conf import settings
from django.contrib.staticfiles.storage import staticfiles_storage
from django.contrib.staticfiles import finders
from django.utils.safestring import mark_safe
__all__ = (
'js',
'css',
)
register = template.Library()
def _get_versioned_file_path(path):
"""
Parametre olarak verilen pathe
dosyanin degistirilme tarihini ekleyip donduruyor.
Ornek girdi: css/style.css
Return: css/style.12212312.css
"""
file_path_regex = re.compile(r"^(.*)\.(.*?)$")
mtime = int(os.path.getmtime(finders.find(path)))
return file_path_regex.sub(r"\1.{}.\2".format(mtime), path)
@register.simple_tag
def js(path, **kwargs):
"""
script tagi olusturan template tagi
Ornek kullanim:
{% js 'js/main.js' %}
{% js 'js/vendors/foo.min.js' versioning=False %}
"""
versioning = kwargs.get('versioning', True)
if settings.ASSET_VERSIONING and versioning:
path = _get_versioned_file_path(path)
result = '<script type="text/javascript" src="{}"></script>'.format(
staticfiles_storage.url(path)
)
return mark_safe(result)
@register.simple_tag
def css(path, **kwargs):
"""
Ornek kullanim:
{% css 'css/style.css' %}
{% css 'css/vendors/foo.min.css' versioning=False %}
"""
versioning = kwargs.get('versioning', True)
if settings.ASSET_VERSIONING and versioning:
path = _get_versioned_file_path(path)
result = '<link rel="stylesheet" href="{}" />'.format(
staticfiles_storage.url(path)
)
return mark_safe(result)
Nginx rewrite rule:
location ~* \.[0-9]+\.(css|js)$ {
rewrite (.*?)\.[0-9]+\.(css|js)$ $1.$2 last;
access_log off;
expires 365d;
}
Bir kac django template tag ve filterlarinin bulundugu bir gist olusturmustum. Isterseniz onlarada bir goz atabilirsiniz.