当只有在更新文件时才强制加载时,Django进行缓存失效处理(cache busting)
首先
当开始全面进行Django的Web应用开发时,会越来越多地处理静态文件,如CSS和JS等,而静态文件的缓存问题则会成为一个令人烦恼的事情。
为了减少页面通信和加载时间,浏览器经常会将静态文件保存在缓存中,并在再次访问时重复使用。因此,即使开发人员对静态文件进行了修改,浏览器可能仍然会使用旧数据而导致显示混乱或出现意外行为。
缓存破解是一种被广泛使用的技巧,它通过在HTML静态文件的路径上添加查询字符串并更改路径,从而确保浏览用户始终加载最新的文件。
通过在Django的设置中添加特定常量或使用名为django-static-md5url的库,可以为静态文件路径添加哈希字符串,并且可以每次都强制重新加载。
然而,如果每次都強制讀取,頁面的加載時間就會變得更長,而且會導致使用者端的快取累積浪費。
因此,這次我們將不使用哈希碼,而是附加文件本身的更新日期,只有在文件更新時才強制進行讀取。
环境
实际的代码
前座: 实施环境建设
我們首先創建一個專案應用程式。
pip install django
django-admin startproject sample_proj
cd sample_proj
django-admin startapp sample_app
文件夹结构
标有“追加”的文件是默认不存在的文件。
标有“不要”的文件本次不使用。
sample_proj/
├── sample_app/
│ ├── static/
│ │ ├── css/
│ │ │ └── index.css: 追加
│ ├── templates/
│ │ └── index.html: 追加
│ ├── templatetags/
│ │ └── cache_busting.py: 追加
│ ├── migrations/
│ ├── admin.py: 不要
│ ├── apps.py
│ ├── models.py: 不要
│ ├── tests.py: 不要
│ ├── urls.py: 追加
│ └── views.py
├── sample_proj/
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
導入之前,各種文件的內容
首先创建要显示的页面数据。
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<link href="{% static 'css/index.css' %}" rel="stylesheet" type="text/css">
<title>Cache busting test</title>
</head>
<body>
<p class="test-text">hogehogefugoooo</p>
</body>
</html>
.test-text {
color: #ff0000;
}
views.py是一个简单的代码,仅用于渲染HTML页面。
from django.shortcuts import render
def index(request, **kwargs):
return render(request, 'index.html')
根据许多Django开发的惯例,我们需要在sample_proj和sample_app中都创建一个urls.py。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
from django.contrib import admin
from django.urls import path, include # includeを追加
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sample_app.urls')), # 追加
]
最后,我们要编辑settings.py文件。在INSTALLED_APPS中添加’sample_app’,以便确保其被识别。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'sample_app', # 追加
]
尝试启动
这次没有定义models.py,但是我们还是先进行迁移吧。
如果在collectstatic时出现错误,通常是因为忘记设置STATIC_ROOT。
python manage.py migrate
python manage.py collectstatic
python manage.py runserver

现在最基本的准备已经完成了。
本题: 缓存破坏的实施方案
好的,从这里开始实现缓存破坏的代码。这次我们将使用与模板引擎兼容性较高的模板标签(simple_tag)来实现。
如果你不了解模板标签(templatetag),我认为你应该查看以下的网站。
将 static_cache.py 文件写入先前的文件夹结构中。
from pathlib import Path
from django import template
from django.conf import settings
from django.templatetags.static import static
register = template.Library()
@register.simple_tag
def static_cache(filepath) -> str:
"""静的ファイルのURLにファイルの更新日時クエリを付加する
"""
# Django標準機能を使ってhtml埋め込み用のパスを取得
res_path = static(filepath)
# mtimeを取得する
full_filepath = Path(getattr(settings, 'STATIC_ROOT', '')).joinpath(filepath)
file_mtime = str(int(full_filepath.stat().st_mtime))
# パスに結合
res_path += '?v=' + file_mtime
return res_path
说明
通过static标签获取静态文件的路径
使用pathlib库(Python标准库)获取实际文件的更新日期和时间
将更新日期和时间截断为整数(转换为秒单位)并作为查询字符串追加
返回编辑后的路径
严格来说,模板标签static在代码中实际上是一个名为do_static()的函数,与本例使用的不同。如果想了解更详细的信息,请查看以下源代码。
https://github.com/django/django/blob/stable/3.2.x/django/templatetags/static.py
我将测试已经实现的模板标签。我要编辑刚才创建的 index.html。
{% load cache_busting %} <!--変更-->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<link href="{% static_cache 'css/index.css' %}" rel="stylesheet" type="text/css"> <!--変更-->
<title>Cache busting test</title>
</head>
<body>
<p class="test-text">hogehogefugoooo</p>
</body>
</html>
请编辑后重新启动应用程序,并确保显示与初始页面内容相同的内容。
查看开发者工具时,可以看到CSS路径中已添加了以下更新日期查询。
<link href="/static/css/index.css?v=1639965343" rel="stylesheet" type="text/css">
确认操作
最后,我们需要确认已经成功实现的缓存破坏机制能够正常运行。
将 index.css 文件进行以下更改。
.test-text {
color: #00ff00;
}
编辑完后,我会执行collectstatic操作。
python manage.py collectstatic

当您在开发者工具中查看CSS文件路径时,会发现与之前不同的查询字符串。
<!-- 先ほどのクエリ文字列は 1639965343 だった -->
<link href="/staticfiles/css/index.css?v=1639967407" rel="stylesheet" type="text/css">
最终
通过使用缓存破坏,即使用户不必每次都清除缓存并重新加载,服务器端也能够强制加载所需的静态文件。在使用静态文件时,请务必尝试引入缓存破坏!