کدی که دور همهی درخواستها میپیچد
Middleware یک لایهی سراسری است: هر درخواست قبل از رسیدن به View از آن عبور میکند و هر پاسخ در بازگشت. کارهایی که به همهی صفحهها مربوطاند جایشان اینجاست: اندازهگیری زمان پاسخ، گرفتن IP واقعی، حالت تعمیرات، افزودن هدرهای امنیتی.
# apps/core/middleware.py
import logging
import time
logger = logging.getLogger("apps.requests")
class RequestTimingMiddleware:
def __init__(self, get_response):
self.get_response = get_response # یک بار، هنگام شروع سرور
def __call__(self, request):
start = time.perf_counter()
response = self.get_response(request) # بقیهی زنجیره و View
ms = (time.perf_counter() - start) * 1000
response["Server-Timing"] = f"app;dur={ms:.0f}"
if ms > 1000:
logger.warning("slow request %s %s %.0fms user=%s",
request.method, request.path, ms, getattr(request.user, "pk", None))
return response
class MaintenanceMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
from django.conf import settings
from django.shortcuts import render
if settings.MAINTENANCE_MODE and not request.path.startswith("/manage-7f3a/"):
return render(request, "503.html", status=503)
return self.get_response(request)
ثبت در MIDDLEWARE: بعد از AuthenticationMiddleware تا request.user در دسترس باشد.
لاگگیری که روی سرور به کار بیاید
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {"std": {"format": "{asctime} {levelname} {name} {message}", "style": "{"}},
"handlers": {
"console": {"class": "logging.StreamHandler", "formatter": "std"},
"file": {"class": "logging.handlers.RotatingFileHandler",
"filename": BASE_DIR / "logs" / "app.log",
"maxBytes": 10 * 1024 * 1024, "backupCount": 5,
"formatter": "std", "encoding": "utf-8"},
},
"loggers": {
"django": {"handlers": ["console", "file"], "level": "WARNING"},
"apps": {"handlers": ["console", "file"], "level": "INFO"},
},
}
با systemd، خروجی console خودکار در journal ذخیره میشود و با journalctl -u carpet -f دیده میشود.
فارسیسازی: i18n
حتی اگر سایت فقط فارسی است، متنهای ثابت کد را با gettext_lazy علامت بزنید تا روزی که نسخهی انگلیسی برای مشتری خارجی لازم شد، فقط فایل ترجمه بسازید:
from django.utils.translation import gettext_lazy as _
class Carpet(models.Model):
name = models.CharField(_("نام طرح"), max_length=100)
# settings.py
LANGUAGES = [("fa", "فارسی"), ("en", "English")]
LOCALE_PATHS = [BASE_DIR / "locale"]
# MIDDLEWARE: "django.middleware.locale.LocaleMiddleware" بعد از SessionMiddleware
python manage.py makemessages -l en
python manage.py compilemessages
نکتههایی که کمتر کسی میداند
__init__middleware فقط یک بار اجرا میشود؛ اگر در آنMiddlewareNotUsedبالا ببرید، جنگو آن middleware را کاملاً از زنجیره حذف میکند (مثلاً وقتی تنظیمی خاموش است).- هدر
Server-Timingمستقیم در تب Network ابزار توسعهی مرورگر نمایش داده میشود؛ بدون هیچ ابزار مانیتورینگی زمان سمت سرور را میبینید. - در سطح ماژول و فیلدهای مدل حتماً
gettext_lazyبنویسید نهgettext؛ نسخهی غیر lazy در زمان import، قبل از فعال شدن زبان، ترجمه میشود. - makemessages روی ویندوز به ابزار GNU gettext نیاز دارد که همراه پایتون نیست؛ یا نصبش کنید یا این مرحله را روی WSL یا سرور لینوکس انجام دهید.
encoding="utf-8"در handler فایل روی ویندوز ضروری است؛ بدون آن اولین پیام لاگ فارسی با UnicodeEncodeError کل لاگگیری را خراب میکند.