یک شب جمعه، ساعت یازده، پروژه‌ای که دوشنبه باید دمو شود و سروری که بالا نمی‌آید. در مرورگر فقط یک پیام کوتاه: ImproperlyConfigured. چند سال بعد، در پروژه‌ای دیگر، همین الگو تکرار شد؛ این بار NoReverseMatch. بعد از کار روی ده‌ها پروژه جنگویی، برایم قطعی شده که خطاهای جنگو (Django) تصادفی نیستند. الگو دارند، خانواده دارند، و ریشه‌شان تقریباً همیشه در یک تصمیم اشتباه معماری یا یک وابستگی ناهماهنگ پنهان است.

چرا خطاهای Django این‌قدر تکرار می‌شوند؟

Django یک فریم‌ورک opinionated است. برخلاف Flask که تقریباً همه‌ی تصمیم‌های ساختاری را به شما واگذار می‌کند، Django مجموعه‌ای از کنوانسیون‌ها را از پیش تحمیل می‌کند: ساختار پروژه، نحوه تعریف اپ‌ها، مدیریت تنظیمات، ترتیب بارگذاری ماژول‌ها، و حتی قراردادهای نام‌گذاری در مدل و URL. همین opinionated بودن، دلیل بزرگ‌ترین مزیت و همچنین بزرگ‌ترین منبع خطاهای Django است.

وقتی از کنوانسیون‌ها تبعیت می‌کنید، همه‌چیز خودکار کار می‌کند. اما کوچک‌ترین انحراف - یک نام اشتباه، یک فایل جابه‌جاشده، یا یک تنظیم فراموش‌شده - باعث می‌شود فریم‌ورک در نقطه‌ای که انتظار ندارید، از کار بیفتد. این دقیقاً همان جایی است که خطاها سر برمی‌آورند.

سه ریشه اصلی برای تکرار خطاها در پروژه‌های واقعی دیده‌ام:

  1. انتظار متفاوت از واقعیت: توسعه‌دهنده فرض می‌کند Django خودش می‌فهمد؛ اما Django فقط چیزی را می‌فهمد که شما صریحاً گفته باشید. جادو در Django وجود ندارد؛ فقط کنوانسیون هست.
  2. محیط‌های ناهمگون: کد در لوکال کار می‌کند، در staging خطا می‌دهد، روی production دوباره کار می‌کند. تفاوت نسخه پایتون، نسخه Django، متغیرهای محیطی، یا حتی نسخه‌ی کتابخانه‌های جانبی، سه‌قلوی همیشگی خطاهای محیطی است.
  3. نبود چرخه‌ی تشخیص سیستماتیک: وقتی خطا رخ می‌دهد، به‌جای پیروی از یک روش قابل تکرار، در صفحات Stack Overflow سرگردان می‌شوید و patch موقتی می‌زنید. patch موقتی، دشمن شماره یک پایداری پروژه است.

برای عمیق‌تر شدن در ساختار کلی این فریم‌ورک، راهنمای راهنمای کامل Django برای بک‌اند را بخوانید. آنجا معماری کلی، انتخاب‌های طراحی و مدل ذهنی درست برای کار با این فریم‌ورک باز شده است.

خطاهای Django شبیه تب هستند: با مسکن پایین می‌آیند، اما تا وقتی علت را نکشید، هر فصل برمی‌گردند.

شش خانواده اصلی خطاها که بیشترین وقت را می‌گیرند

در تجربه‌ی من روی ده‌ها پروژه Django، خطاها را می‌توان در شش خانواده طبقه‌بندی کرد. شناخت این خانواده‌ها، کلید تشخیص سریع است. وقتی پیامی در ترمینال یا مرورگر می‌بینید، اول مشخص کنید به کدام خانواده تعلق دارد. این کار نیمی از راه‌حل است، چون جهت جستجو و دیباگ را از ابتدا درست تعیین می‌کند.

  1. خطاهای پیکربندی (Configuration Errors): فریم‌ورک بوت می‌شود ولی در وسط راه به تنظیمات نامعتبر برمی‌خورد.
  2. خطاهای Import: ماژولی که انتظار می‌رود وجود ندارد، یا در جای اشتباه قرار گرفته است.
  3. خطاهای دیتابیس: مهاجرت‌ها (Migrations) هماهنگ نیستند، یا کوئری‌ها به شکل نامنتظره عمل می‌کنند.
  4. خطاهای Template و URL: مسیرهای تعریف‌شده با آنچه در کد استفاده می‌شود، همخوان نیستند.
  5. خطاهای View و فرم: منطق view اشتباه است یا داده‌های ورودی مطابق انتظار نیست.
  6. خطاهای امنیتی: CSRF، PermissionDenied، و SuspiciousOperation که نشان می‌دهند لایه امنیتی Django فعال است و کار خودش را می‌کند.

خطاهای پیکربندی و راه‌اندازی Django

ImproperlyConfigured

این خطا شاید پرتکرارترین پیام تازه‌کارها باشد، ولی خیلی از توسعه‌دهنده‌های حرفه‌ای هم هر چند وقت یک بار به آن برمی‌خورند. Django در سطح بوت، اعتبارسنجی‌های دقیقی انجام می‌دهد. اگر یکی از این موارد را نقض کنید، ImproperlyConfigured بالا می‌آید:

  • نبود SECRET_KEY یا خالی بودن آن
  • نبود ROOT_URLCONF یا مسیر اشتباه آن
  • تنظیمات INSTALLED_APPS ناقص یا اشتباه
  • نبود DATABASES یا تنظیمات اشتباه در آن
  • عدم تعریف ALLOWED_HOSTS در حالت DEBUG = False
  • فراخوانی django.setup() در جای اشتباه

تشخیص: پیام خطا دقیقاً می‌گوید کدام تنظیم مشکل دارد. جمله را از آخر به اول بخوانید. معمولاً هسته‌ی مشکل در سه-چهار کلمه آخر است.

راه‌حل: معمولاً استفاده از یک فایل تنظیمات مبتنی بر محیط (Environment-based Settings) مشکل را برای همیشه رفع می‌کند. اگر ساختار پروژه را تازه شروع می‌کنید، راهنمای Django برای پروژه‌های پایتونی الگوی استاندارد را نشان می‌دهد.

Verification: بعد از اصلاح، پروژه را با python manage.py check اجرا کنید. اگر پیامی برنگشت، تنظیمات معتبر است. برای اطمینان بیشتر، همان دستور را با --deploy هم اجرا کنید تا هشدارهای آماده‌سازی production دیده شوند.

DisallowedHost

پیام خطا: Invalid HTTP_HOST header. دلیل: درخواست از هاستی می‌آید که در ALLOWED_HOSTS نیست. این خطا دو سناریو دارد:

  1. توسعه‌ی لوکال: باید localhost و 127.0.0.1 در ALLOWED_HOSTS باشند. اگر از دامنه‌ی ngrok یا مشابه استفاده می‌کنید، آن دامنه هم باید اضافه شود.
  2. استقرار: دامنه‌ی اصلی و زیردامنه‌ها باید اضافه شده باشند. اگر پروژه پشت proxy یا load balancer است، باید هدرهای واقعی هم بررسی شوند.

نکته‌ی حرفه‌ای: در محیط توسعه، اگر می‌خواهید همه‌ی هاست‌ها را بپذیرید، می‌توانید از ALLOWED_HOSTS = ["*"] استفاده کنید. اما هرگز این مقدار را در production نگه ندارید. این یک نقص امنیتی جدی است که به حملات Host Header Injection باز می‌کند.

ساده‌ترین راه نجات، معمولاً گران‌ترین راه آینده است. ستاره‌ی ALLOWED_HOSTS در production یکی از همین دام‌هاست.

AppRegistryNotReady

پیام خطا: Apps aren"t loaded yet یا django.core.exceptions.AppRegistryNotReady. این خطا وقتی رخ می‌دهد که مدلی را قبل از بارگذاری اپلیکیشن‌ها import کنید. مثلاً در فایل settings.py یا در یک ماژول که قبل از اپ‌ها بارگذاری می‌شود.

راه‌حل: import مدل‌ها را به داخل تابع منتقل کنید، نه بالای فایل. الگوی درست:

def get_some_data():
    from myapp.models import MyModel
    return MyModel.objects.all()

Verification: پس از جابه‌جایی import، پروژه را با python manage.py runserver اجرا کنید و بررسی کنید که خطا برطرف شده باشد. اگر خطا باقی ماند، دنبال importهای سطح ماژول در فایل‌های بالادست بگردید.

ImproperlyConfigured در تنظیمات اپ‌ها

یک حالت خاص که کمتر کسی از ابتدا می‌شناسد: وقتی یک اپ را در INSTALLED_APPS به شکل اشتباه اضافه می‌کنید. مثلاً اپ به‌صورت myapp اضافه شده ولی اپ واقعی در مسیر apps.myapp قرار دارد. Django در بوت، هنگام apps.populate() به این ناسازگاری برمی‌خورد.

راه‌حل: مسیر دقیق اپ را در INSTALLED_APPS استفاده کنید. اگر از ساختار چند-پوشه‌ای استفاده می‌کنید، نام app config یا مسیر کامل ماژول را وارد کنید. برای پروژه‌های بزرگ، استفاده از AppConfig سفارشی و default_auto_field مناسب توصیه می‌شود.

خطاهای Import و بارگذاری ماژول

ModuleNotFoundError

پیام خطا: No module named "X". این خطا سه ریشه اصلی دارد:

  1. پکیج واقعاً نصب نیست: راه‌حل pip install یا pip install -r requirements.txt.
  2. پکیج نصب است ولی در virtualenv دیگری: راه‌حل، بررسی which python و which pip و فعال‌سازی درست virtualenv. این خطا در تیم‌های چندنفره شایع‌تر از آن است که فکرش را می‌کنید.
  3. مسیر پروژه در PYTHONPATH نیست: راه‌حل، اجرای پروژه از ریشه، یا افزودن مسیر به sys.path.

برای درک عمیق‌تر این خطا در زبان پایتون، مقاله رفع خطای ModuleNotFoundError را ببینید.

ImportError و وابستگی دایره‌ای

پیام خطا: cannot import name "X" from "Y". این خطا با ModuleNotFoundError فرق دارد. یعنی ماژول پیدا شده، اما نام مورد نظر در آن نیست. دلایل:

  1. تایپو در نام تابع یا کلاس
  2. وابستگی دایره‌ای (Circular Import)
  3. نسخه‌ی ناسازگار یک پکیج که API را تغییر داده

Circular Import رایج‌ترین و مرموزترین شکل این خطاست. دو ماژول که به هم import می‌زنند، در بارگذاری اولیه به بن‌بست می‌خورند. تشخیص: دنبال importهای دوطرفه در پروژه بگردید. راه‌حل: انتقال import به داخل تابع، یا استفاده از الگوی Lazy Import.

# اشتباه - circular import
# file a.py
from b import get_b
def get_a():
    return get_b()

# file b.py
from a import get_a
def get_b():
    return get_a()

# درست - lazy import
# file a.py
def get_a():
    from b import get_b
    return get_b()

برای مبانی و تفاوت‌ها، مقاله علت و رفع ImportError در پایتون را بخوانید.

وابستگی دایره‌ای، شبیه به ازدواج دو قهرمانی است که هر کدام منتظر بلند شدن دیگری است. تا یکی از آن دو، دست از import نکشد، بن‌بست ادامه دارد.

ImproperlyConfigured در Django REST Framework

اگر با DRF کار می‌کنید، یک خطای خاص ظاهر می‌شود: Cannot apply DjangoModelPermissions on a view that does not have .queryset or .get_queryset() method. ریشه، پیکربندی ناقص کلاس permission است. راه‌حل: queryset یا get_queryset() را در view تعریف کنید، یا از یک کلاس permission مناسب استفاده کنید.

خطاهای دیتابیس و مدل‌ها

OperationalError: no such table

این خطا در چند سناریو ظاهر می‌شود:

  1. در تست: دیتابیس تست تازه ساخته شده و migrate نشده. راه‌حل: مطمئن شوید که در تست، migrations اجرا می‌شوند. اگر تست‌های سریع می‌خواهید، از --keepdb استفاده کنید یا MIGRATION_MODULES را در تست override کنید.
  2. در production: migration اجرا نشده. راه‌حل: python manage.py migrate روی محیط هدف.
  3. در staging: دیتابیس از یک snapshot بازیابی شده که جدول جدید را ندارد.

نکته‌ی مهم: هرگز در production دستور makemigrations را بدون بررسی اجرا نکنید. در staging ابتدا خروجی را با --dry-run بررسی کنید.

برای مبانی دیتابیس، آموزش یادگیری MySQL از صفر نقطه شروع مطمئنی است. همچنین اگر خطاهای دیتابیس شما از لایه SQL می‌آید، خطاهای رایج MySQL فهرست جامعی از کدها و پیام‌ها را دارد.

DoesNotExist و MultipleObjectsReturned

پیمایش مدل‌ها در Django با دو استثنای خاص همراه است:

  • Model.DoesNotExist وقتی get() هیچ رکوردی پیدا نکند
  • Model.MultipleObjectsReturned وقتی get() بیش از یک رکورد پیدا کند

هر دو در ذات خود خطا نیستند؛ نشانه‌ی این هستند که فرض‌های شما با داده‌ها همخوان نیست. راه‌حل‌های درست:

  • جای get() از filter().first() استفاده کنید اگر انتظار صفر یا یک رکورد دارید.
  • اگر get() را لازم دارید، آن را در بلوک try/except قرار دهید.
  • برای MultipleObjectsReturned، شرط فیلتر را دقیق‌تر کنید یا در مدل، UniqueConstraint مناسب اضافه کنید.

Migration Conflicts

پیام خطا: Conflicting migrations detected. این خطا در تیم‌های چندنفره بسیار رایج است. دو developer هم‌زمان روی یک مدل کار می‌کنند و هر کدام migration خودشان را می‌سازند.

راه‌حل اصولی:

  1. python manage.py makemigrations --merge برای ادغام
  2. بررسی دستی فایل‌های migration - مخصوصاً ترتیب عملیات
  3. اجرای migrate و تست

راه‌حل پیشگیرانه: قبل از هر commit، makemigrations و migrate روی شاخه لوکال خودتان. بعد pull و دوباره migrate. اضافه کردن یک CI job که روی PR بررسی کند migrationها conflicting نیستند، این درد را تقریباً کامل حل می‌کند.

ValueError: Field expected a number but got

این خطا معمولاً در فرم‌ها یا serializerها رخ می‌دهد. یعنی داده‌ای که به مدل می‌رسد، نوع اشتباه دارد. تشخیص: message خطا به شما می‌گوید کدام فیلد و چه مقداری. راه‌حل: validation در سطح فرم یا serializer اضافه کنید تا ورودی‌های نامعتبر پیش از رسیدن به مدل رد شوند. این کار، لایه‌ی دفاعی مهمی است که از خطاهای پنهان و پیچیده‌تر جلوگیری می‌کند.

OperationalError: FATAL: too many connections

در پروژه‌های پربازدید یا در تست‌های موازی، این خطا شایع است. ریشه: نشت اتصال (connection leak) یا نبود connection pooling مناسب. راه‌حل: تنظیم CONN_MAX_AGE در تنظیمات دیتابیس، استفاده از pgbouncer برای PostgreSQL، و اطمینان از اینکه اتصال‌ها در سطح ترد بسته می‌شوند.

خطاهای Template و URL

TemplateDoesNotExist

پیام خطا: X.html not found. ریشه‌های رایج:

  1. مسیر اشتباه در TEMPLATES["DIRS"] در settings
  2. نبود اپ مربوطه در INSTALLED_APPS
  3. اشتباه در نام پوشه - template به‌جای templates
  4. نبود پوشه‌ی templates در ریشه‌ی اپ یا نبود فایل مورد نظر

نکته‌ی ظریف: Django فایل‌های template را در چند مسیر جستجو می‌کند. ترتیب جستجو را می‌توانید در DEBUG با خطای رخ‌داده ببینید. خود پیام خطا، لیست مسیرهای بررسی‌شده را چاپ می‌کند. این تنها جایی است که باید از پیام خطا به‌عنوان مستندات واقعی استفاده کنید.

NoReverseMatch

پیام خطا: Reverse for "X" not found. این خطا در سه لایه رخ می‌دهد:

  • در url tag داخل template
  • در redirect() داخل view
  • در reverse() و reverse_lazy() در کد پایتون

دلایل رایج: تایپو در نام URL، اشتباه در ارسال پارامتر، و تغییر نام URL بدون به‌روزرسانی همه‌ی مراجع. رایج‌ترین حالت: در url نام URL را انگلیسی نوشتید، در template فارسی. Django به حساسیت حروف هم اهمیت می‌دهد.

راه‌حل: برای URLهای پرکاربرد، از get_absolute_url در مدل استفاده کنید. این تضمین می‌کند که مرجع URL همیشه از یک نقطه مدیریت شود و تغییرات به همه‌ی جاها سرایت کند.

Static Files Not Loading در DEBUG=False

در حالت DEBUG=False، معمولاً فایل‌های static بارگذاری نمی‌شوند. دلیل: در این حالت Django فایل‌ها را سرو نمی‌کند. راه‌حل: استفاده از WhiteNoise، یا تنظیم Nginx برای سرو مستقیم فایل‌های static. قبل از هر راه‌حل، مطمئن شوید که collectstatic اجرا شده و فایل‌ها در STATIC_ROOT قرار دارند.

Verification: بعد از تنظیم، درخواست HTTP برای یک فایل static بزنید و کد وضعیت را بررسی کنید. اگر 200 برگشت، تنظیمات درست است. اگر 404، مسیر یا permission اشتباه است.

خطاهای View، فرم و امنیت

CSRF Verification Failed

پیام خطا: Forbidden (403). CSRF verification failed. این خطا بیشتر وقت‌ها گمراه‌کننده است. ریشه‌های رایج:

  1. فرم فاقد {% csrf_token %} است
  2. کوکی CSRF حذف یا مسدود شده - مثلاً توسط مرورگر کاربر یا به‌دلیل same-site policy
  3. درخواست از دامنه‌ی متفاوت با اختلاف پروتکل HTTP/HTTPS ارسال شده
  4. پشت proxy یا load balancer، Django هدر اصلی را نمی‌بیند

راه‌حل: تنظیم CSRF_TRUSTED_ORIGINS برای دامنه‌های قابل اعتماد. نکته‌ی مهم: هرگز @csrf_exempt را به‌عنوان راه‌حل عمومی روی viewها نگذارید. این کار لایه‌ی حفاظتی مهمی را حذف می‌کند و درگاه حمله CSRF را باز می‌گذارد.

ValidationError

این خطا در سه سطح ظاهر می‌شود:

  • در فیلد مدل: full_clean() روی instance
  • در فرم: is_valid() برمی‌گردد False
  • در serializerهای DRF: خطای ساختارمند با کد

نکته‌ی حرفه‌ای: ValidationError برای خطاهای کاربر است، نه باگ برنامه. بنابراین باید به‌صورت خوانا به کاربر نمایش داده شود، نه در لاگ سرور. اگر ValidationError را در لاگ می‌بینید، احتمالاً منطق validation شما ناقص است.

RelatedObjectDoesNotExist

پیام خطا: User has no profile یا مشابه آن. این خطا وقتی رخ می‌دهد که به یک relation یک‌به‌یک (OneToOne) دسترسی می‌زنید که هنوز ساخته نشده.

راه‌حل‌ها:

  • استفاده از hasattr قبل از دسترسی
  • استفاده از get_or_create برای ساخت خودکار
  • استفاده از signal post_save برای ساخت پروفایل هنگام ساخت کاربر

PermissionDenied

پیام خطا: 403 Forbidden. این خطا نشان می‌دهد Django درست کار می‌کند. کاربری که درخواست داده، مجوز لازم را ندارد. راه‌حل: بررسی permissions کاربر، بررسی منطق authorize در view، و اطمینان از اینکه login_required یا PermissionRequiredMixin در جای درست قرار دارند.

SuspiciousOperation

پیام‌های SuspiciousOperation نشان می‌دهند که Django یک رفتار مشکوک را شناسایی کرده است. مثال‌ها:

  • SuspiciousFileOperation: تلاش برای دسترسی به فایلی خارج از MEDIA_ROOT
  • DisallowedModelAdminLookup: درخواست مشکوک به admin
  • DisallowedRedirect: تلاش برای redirect به دامنه‌ی خارجی

نکته‌ی مهم: هرگز برای رفع این خطاها، این حفاظت‌ها را غیرفعال نکنید. SuspiciousOperation یعنی یک لایه امنیتی واقعی جلوی یک رفتار مشکوک را گرفته است. اگر می‌بینید این خطا زیاد تکرار می‌شود، احتمالاً کد شما الگوی اشتباهی دارد، نه اینکه Django بیش از حد سختگیر است.

برای مرور جامع امنیت جنگو، مقاله بهترین روش‌های امنیت Django را بخوانید.

SuspiciousOperation را به‌عنوان مزاحم نبینید؛ آن نگهبانی است که فقط وقت واقعی سروصدا می‌کند.

ImproperlyConfigured در DRF

در Django REST Framework، خطای ImproperlyConfigured در چند نقطه‌ی مشخص شایع است: نبود serializer_class در generic view، تنظیم ناقص authentication_classes، یا نبود queryset برای ModelViewSet. تشخیص، همان پیام دقیق DRF است. راه‌حل: از الگوی get_serializer_class() برای انتخاب پویا و از get_queryset() برای محدودسازی داده استفاده کنید.

TypeError در منطق View

گاهی خطاهای نوعی پایتون، درون منطق view رخ می‌دهند و در traceback به‌عنوان خطای سطح پایتون دیده می‌شوند. مثال کلاسیک: NoneType object is not subscriptable وقتی یک فیلد نال از دیتابیس برمی‌گردد. راه‌حل اصولی: در سطح serializer یا model method، مقدار پیش‌فرض مناسب تعیین کنید، نه اینکه در view با شرط‌های پراکنده پوشش دهید.

OperationalError: database is locked

در SQLite، این خطا شایع است. ریشه: تراکنش طولانی یا نوشتن هم‌زمان. راه‌حل: برای SQLite، از timeout در تنظیمات استفاده کنید. اما اگر پروژه در production است، SQLite انتخاب اشتباهی برای بار نوشتن است و باید به PostgreSQL مهاجرت کنید. این خطا نشانه‌ی درست انتخاب نکردن دیتابیس است، نه یک باگ کدی.

جدول تشخیص سریع خطاها

این جدول، خلاصه‌ی تشخیص را در یک نگاه ارائه می‌دهد. در پروژه‌های واقعی، این جدول را در ویکی تیم بگذارید تا همه به آن دسترسی داشته باشند.

خطا خانواده اولین جایی که باید نگاه کنید
ImproperlyConfigured پیکربندی settings.py و متغیرهای محیطی
DisallowedHost پیکربندی ALLOWED_HOSTS و CSRF_TRUSTED_ORIGINS
AppRegistryNotReady پیکربندی importهای سطح ماژول
ModuleNotFoundError Import virtualenv و requirements.txt
ImportError دایره‌ای Import importهای دوطرفه بین ماژول‌ها
OperationalError (no such table) دیتابیس migrate و اتصال دیتابیس
Migration Conflicts دیتابیس شاخه‌های migration و merge
NoReverseMatch URL urls.py و نام‌های URL
TemplateDoesNotExist Template TEMPLATES["DIRS"] و INSTALLED_APPS
CSRF Verification Failed امنیت csrf_token و CSRF_TRUSTED_ORIGINS
PermissionDenied امنیت permissions و decoratorها
SuspiciousOperation امنیت MEDIA_ROOT و مسیرهای فایل

پرسش‌های پرتکرار درباره خطاهای Django

چرا خطاهای Django در لوکال ظاهر نمی‌شوند اما در production بالا می‌آیند؟

پاسخ: چون سه چیز در لوکال و production یکسان نیستند. اول، مقدار DEBUG: در لوکال معمولاً True است و بسیاری از خطاها پنهان می‌شوند. دوم، ALLOWED_HOSTS که در لوکال شامل localhost است ولی در production نیاز به دامنه دارد. سوم، نسخه‌ی پکیج‌ها و متغیرهای محیطی. راه‌حل: staging را با تنظیمات نزدیک به production بسازید. الگوی درست در ساخت REST API با پایتون آمده است.

آیا خاموش کردن DEBUG برای دیباگ کردن درست است؟

پاسخ: بله، ولی فقط به‌صورت موقت و در محیط staging. در production، DEBUG هرگز نباید True باشد. اگر با DEBUG=False خطا می‌گیرید، لاگ‌های سرور را بررسی کنید یا ابزارهایی مثل Sentry را فعال کنید. Django در DEBUG=True اطلاعات حساس را نمایش می‌دهد که در production خطر امنیتی جدی است.

چطور بفهمم خطا از Django است یا از پکیج جانبی؟

پاسخ: در traceback دقت کنید. اگر آخرین frame در پکیج جانبی است و پکیج از API داخلی Django استفاده کرده، اغلب مشکل از ناسازگاری نسخه است. راه‌حل: pip list و بررسی نسخه‌ها با requirements اصلی پکیج. برای دیباگ عمیق‌تر، لاگ سطح DEBUG را موقتاً فعال کنید.

آیا خطاهای Django معمولاً از پایتون می‌آیند یا از خود فریم‌ورک؟

پاسخ: تقریباً همیشه از یک سطح بالاتر می‌آیند. خطاهای سطح پایتون مثل TypeError، AttributeError، یا KeyError در منطق شما ریشه دارند، نه در Django. برای شناخت این خطاها، آموزش یادگیری پایتون از صفر را ببینید. خطاهای سطح جنگو معمولاً پیکربندی یا معماری را نشان می‌دهند.

چرا بعد از ارتقای نسخه Django، پروژه دیگر کار نمی‌کند؟

پاسخ: چون Django در نسخه‌های major، APIهای قدیمی را deprecate و حذف می‌کند. راه‌حل: قبل از ارتقا، changelog و release notes را بخوانید. از ابزارهایی مثل django-upgrade برای به‌روزرسانی خودکار کد استفاده کنید. همیشه در staging ابتدا تست کنید.

چرا خطای Circular Import بعد از اضافه کردن یک import جدید رخ می‌دهد؟

پاسخ: چون import شما ترتیب بارگذاری ماژول‌ها را تغییر داده و یک حلقه‌ی قبلاً پنهان را آشکار کرده است. راه‌حل: import را به داخل تابع منتقل کنید، یا از string-based app label در ForeignKey استفاده کنید. برای درک عمیق‌تر، الگوهای بارگذاری در جنگو را مطالعه کنید.

چرا در تیم‌های چندنفره، خطاهای migration این‌قدر زیاد است؟

پاسخ: چون دو developer می‌توانند به‌طور هم‌زمان migration بسازند که روی یک مدل اثر می‌گذارند. راه‌حل: قبل از هر commit روی مدل، pull از شاخه اصلی، و ادغام migrationها با makemigrations --merge. هرگز فایل migration تولیدشده را دستی ویرایش نکنید، مگر اینکه کاملاً به ساختار آن مسلط باشید.

کدام ابزارها برای ردیابی خطاهای production جنگو مناسب هستند؟

پاسخ: Sentry برای exception tracking، Prometheus و Grafana برای مانیتورینگ، و ELK Stack برای log aggregation. برای پروژه‌های کوچک، لاگ‌های فایل کافی است. اما در هر اندازه، لاگ‌ها باید ساختارمند باشند تا قابل جستجو باشند. برای مبانی معماری، بک‌اند چیست را ببینید.

آیا باید Exceptionها را در View خودم catch کنم یا بگذارم Django هندل کند؟

پاسخ: بستگی دارد. برای خطاهای غیرمنتظره، بگذارید Django هندل کند - از django.views.defaults استفاده کنید. برای خطاهای قابل پیش‌بینی مثل ValidationError یا DoesNotExist، در View catch کنید و پاسخ معنادار به کاربر برگردانید. الگوی اصولی مدیریت استثنا در مدیریت خطا در پایتون آمده است.

چرا بعد از فعال‌سازی HTTPS، خطاهای CSRF بیشتر شده‌اند؟

پاسخ: چون Kubernetes یا proxy در جلوی اپ قرار دارد و Django نمی‌تواند پروتکل اصلی را تشخیص دهد. راه‌حل: تنظیم SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") و اضافه کردن دامنه به CSRF_TRUSTED_ORIGINS با پیشوند https. بدون این تنظیم، Django درخواست HTTPS را به‌عنوان HTTP می‌بیند و CSRF را رد می‌کند.

آیا استفاده از try/except در همه Viewها توصیه می‌شود؟

پاسخ: نه. try/except با دامنه‌ی گسترده، خطاهای واقعی را پنهان می‌کند و دیباگ را سخت‌تر می‌کند. تنها خطاهای قابل پیش‌بینی را catch کنید. خطاهای دیگر باید در لاگ ظاهر شوند و توسط ابزارهای مانیتورینگ دیده شوند.

تفاوت ImproperlyConfigured و ConfigurationError چیست؟

پاسخ: در Django، تنها ImproperlyConfigured وجود دارد. اگر خطای مشابهی با نام ConfigurationError می‌بینید، احتمالاً از یک پکیج جانبی می‌آید. الگوی تشخیص یکسان است: پیام دقیق خطا را بخوانید و اولین پارامتر نامعتبر را اصلاح کنید.

چرا بعد از فعال‌سازی CSRF_COOKIE_SECURE، کاربران لاگین نمی‌شوند؟

پاسخ: چون این تنظیم باعث می‌شود کوکی فقط روی HTTPS ارسال شود. اگر سایت شما HTTP است یا از پروکسی با هدر اشتباه استفاده می‌کند، کاربر نمی‌تواند لاگین کند. راه‌حل: مطمئن شوید سایت روی HTTPS است و SECURE_PROXY_SSL_HEADER درست تنظیم شده است.

آیا خطاهای Django همیشه در سرور رخ می‌دهند یا در مرورگر هم دیده می‌شوند؟

پاسخ: خطاهای سمت سرور مثل ImproperlyConfigured یا OperationalError در Django با صفحه‌ی خطای مخصوص نمایش داده می‌شوند (اگر DEBUG=True باشد) یا در لاگ سرور ظاهر می‌شوند. خطاهای سمت مرورگر مثل CSRF Verification Failed در خود مرورگر دیده می‌شوند. برای دیباگ، همیشه به هر دو سمت نگاه کنید.

چطور می‌توانم خطاهای Django را قبل از کاربر ببینم؟

پاسخ: سه لایه‌ی دفاعی بگذارید. اول، Sentry برای گرفتن خطاهای production. دوم، CI job که تست‌ها و python manage.py check --deploy را اجرا می‌کند. سوم، monitoring uptime برای اطلاع از downtime. با این سه لایه، تقریباً همیشه قبل از کاربر خبردار می‌شوید.

درس‌های میدانی از دل خطاهای جنگو

خطاهای Django، در ظاهر متنوع، اما در باطن خانوادگی‌اند. اگر بخواهم در چند جمله خلاصه کنم، سه اصل تجربی برایم تعیین‌کننده بوده است:

اصل اول: پیام خطا را کامل بخوانید. نه فقط خط اول را. در نه مورد از ده مورد، پاسخ در سه خط آخر traceback است. توسعه‌دهنده‌های تازه‌کار عادت دارند سریع به سراغ جستجو در گوگل بروند، ولی خود Django، پیام‌هایش را با دقت طراحی کرده است. هر خطی که از traceback می‌خوانید، یک فرض پنهان را افشا می‌کند.

اصل دوم: تفاوت محیط‌ها را بپذیرید. کدی که در لوکال کار می‌کند، ممکن است در staging خطا بدهد و در production باز کار کند. این نه جادو است و نه نفرین؛ تفاوت پیکربندی و وابستگی‌هاست. هر تغییر در محیط، باید با چک‌لیست بررسی شود. تفاوت‌های محیطی، دلیل شماره یک خطاهایی است که فقط یک نفر از تیم می‌بیند.

اصل سوم: چرخه‌ی تشخیص را مستند کنید. هر بار که خطایی رفع می‌کنید، در commit message یا یک فایل تیم بنویسید چطور پیدا شد. با گذشت زمان، الگوها روشن می‌شوند و سرعت تشخیص چند برابر می‌شود. تیمی که این کار را می‌کند، در شش ماه اول، زمان دیباگ خود را به نصف می‌رساند.

خطاهای Django بیشتر شبیه مربی هستند تا دشمن. هر خطا، یک فرض پنهان را افشا می‌کند. اگر با دقت گوش کنید، بعد از چند ماه، پروژه‌های بعدی شما با خطاهای کمتری متولد می‌شوند. این دقیقاً همان چیزی است که تجربه را از دانش جدا می‌کند: تشخیص الگو، نه فقط رفع علائم.

هدف این مقاله، تمام‌کردن فهرست خطاهای Django نبود. هدف، دادن یک چارچوب ذهنی بود که با آن بتوانید هر خطای جدیدی را سریع دسته‌بندی کنید، ریشه‌اش را پیدا کنید، و با یک راه‌حل پایدار به‌جای patch موقتی رفعش کنید. وقتی این چارچوب را درونی کنید، خطاها از مزاحم به یک جریان عادی کار تبدیل می‌شوند.

اگر این خطاها را در یک پروژه واقعی دیده‌اید و یکی از آن‌ها وقت بیشتری از شما گرفته است، برایم جالب است بدانم کدام یک. تجربه‌تان را در دیدگاه‌ها بنویسید؛ به‌ویژه اگر راه‌حل متفاوتی پیدا کرده‌اید که می‌تواند برای خواننده بعدی مفید باشد. 🐍