سریعترین کوئری، کوئریای است که اجرا نشود
صفحهی اصلی فروشگاه که پرفروشترین طرحها را با چند annotate سنگین نشان میدهد، در هر بازدید همان نتیجه را دوباره حساب میکند. کش یعنی نتیجه را یک بار حساب کنیم و برای مدتی نگه داریم. جنگو چهار سطح کش دارد و از نسخهی 4.0 backend رسمی Redis هم دارد.
# settings.py (pip install redis)
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": os.getenv("REDIS_URL", "redis://127.0.0.1:6379/1"),
"KEY_PREFIX": "carpet",
"TIMEOUT": 300,
}
}
سطح ۱: کش کل View
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
@cache_page(60 * 10) # ۱۰ دقیقه
def price_list(request): ...
# برای CBV در urls:
path("catalog/", cache_page(600)(CatalogView.as_view()), name="catalog")
سطح ۲: کش تکهای از قالب
{% load cache %}
{% cache 900 bestsellers %}
{% for carpet in bestsellers %}<li>{{ carpet.name }}</li>{% endfor %}
{% endcache %}
{% cache 300 user_sidebar request.user.pk %}...{% endcache %} {# کلید جدا برای هر کاربر #}
سطح ۳: کش سطح پایین
from django.core.cache import cache
def get_bestsellers():
return cache.get_or_set(
"bestsellers:v1",
lambda: list(Carpet.objects.filter(status="active")
.annotate(sold=Sum("orderitem__quantity"))
.order_by("-sold")[:10]),
timeout=900,
)
# باطل کردن هنگام تغییر
def on_carpet_changed(**kwargs):
cache.delete("bestsellers:v1")
نکتهی مهم list(...) است: اگر خود QuerySet را کش کنید، چون تنبل است ممکن است چیزی که ذخیره میشود فقط «توصیف کوئری» باشد و در هر استفاده دوباره اجرا شود.
| سطح | ابزار | مناسب برای |
|---|---|---|
| کل سایت | UpdateCacheMiddleware / FetchFromCacheMiddleware | سایتهای تقریباً ایستا و بدون لاگین |
| View | cache_page | صفحات عمومی مثل لیست قیمت |
| قالب | {% cache %} | بخشهای سنگین یک صفحهی پویا |
| داده | cache.get_or_set | نتیجهی محاسبات و فراخوانی API بیرونی |
نکتههایی که کمتر کسی میداند
cache_pageرا روی صفحهای که نام کاربر یا توکن CSRF دارد نگذارید؛ کاربر دوم صفحهی کاربر اول را میبیند. اگر لازم است، باvary_on_cookieکلید را به کوکی وابسته کنید.KEY_PREFIXاجازه میدهد چند پروژه یک Redis مشترک داشته باشند بدون تداخل کلیدها؛ روی سرورهای کوچک ایرانی که یک Redis برای همه دارند، ضروری است.- بهجای پاک کردن تکتک کلیدها، یک عدد نسخه در نام کلید بگذارید (
bestsellers:v2)؛ عوض کردن نسخه همهی کش قدیمی را بیاثر میکند. cache.add()فقط اگر کلید وجود نداشته باشد مینویسد و اتمیک است؛ پایهی یک قفل ساده برای جلوگیری از اجرای همزمان یک کار سنگین.- در تستها از
DummyCacheیاLocMemCacheاستفاده کنید و بین تستهاcache.clear()بزنید؛ وگرنه نتیجهی یک تست به تست بعدی نشت میکند.