فصل ۷: API و امکانات حرفه‌ای — DRF، کش، Celery و امنیت

کش: per-view، fragment، low-level و Redis

سریع‌ترین کوئری، کوئری‌ای است که اجرا نشود

صفحه‌ی اصلی فروشگاه که پرفروش‌ترین طرح‌ها را با چند 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سایت‌های تقریباً ایستا و بدون لاگین
Viewcache_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() بزنید؛ وگرنه نتیجه‌ی یک تست به تست بعدی نشت می‌کند.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.