۲۰۲۶.۱۰.۰۹ · دیدگاه‌های آرویون

سوالات مصاحبه Django: ۳۰ پرسش با پاسخ کوتاه

۳۰ پرسش مصاحبه Django از ORM و مایگریشن تا امنیت، تست و استقرار، هر کدام با پاسخی کوتاه که بر پایهٔ مستندات رسمی نوشته شده است.

تصویر سوالات مصاحبه Django: ۳۰ پرسش با پاسخ کوتاه

برای آماده شدن برای سوالات مصاحبه Django لازم نیست صدها پرسش را حفظ کنید؛ مهم‌تر این است که مفهوم‌ها را بفهمید و بدانید هر کدام کجا دردسر درست می‌کند. این ۳۰ پرسش در نُه محور مرتب شده‌اند: ORM، مدل و مایگریشن، ویو و URL، قالب و فرم، امنیت، احراز هویت و نشست، کارایی و کش، تست و استقرار. انتخاب پرسش‌ها سلیقه‌ای است، نه فهرست کامل آنچه در مصاحبه‌ها پرسیده می‌شود؛ پاسخ‌ها کوتاه‌اند و بر پایهٔ مستندات رسمی Django نوشته شده‌اند.

مصاحبه‌گر با این پرسش‌ها چه چیزی را می‌سنجد؟

برای نمونه پرسش «QuerySet تنبل است یعنی چه؟» در عمل می‌پرسد: آیا می‌دانید کوئری دقیقاً کجا اجرا می‌شود و چه چیزی باعث اجرای دوبارهٔ آن می‌شود؟ دسترسی به رابطه‌ها در حلقه و کوئری‌های اضافهٔ ناشی از آن، پرسش جداگانه‌ای است. کنار هر پاسخ یک مثال و یک «چه وقت خراب می‌شود» هم آماده کنید؛ این پیشنهاد آمادگی است، نه آماری دربارهٔ بازار.

پاسخ‌ها بر پایهٔ مستندات Django 5.2 LTS نوشته شده‌اند. تا زمان نگارش، طبق صفحهٔ دانلود Django، دورهٔ پشتیبانی اصلی 5.2 پایان یافته و این نسخه تا آوریل ۲۰۲۸ فقط پشتیبانی تمدیدشده (اصلاحات امنیتی و جلوگیری از از دست رفتن داده) دارد؛ نسخه‌های 6.0 و 6.1 هم منتشر شده‌اند. مستندات 4.2 نیز هشدار می‌دهد که آن نسخه دیگر پشتیبانی نمی‌شود. اگر پروژهٔ شما روی نسخهٔ دیگری است، جزئیات را با مستندات همان نسخه مقایسه کنید.

۳۰ سوال مصاحبه Django با پاسخ کوتاه

ORM و کوئری‌ها

۱. «تنبل» بودن QuerySet یعنی چه و چه چیزی آن را اجرا می‌کند؟

ساختن، فیلتر و زنجیره کردن QuerySet‌ها هنوز به پایگاه داده نمی‌رسد. کوئری وقتی اجرا می‌شود که QuerySet را پیمایش کنید یا مثلاً list()، len()، bool() یا repr() روی آن بزنید. پس از ارزیابی کامل، نتیجه کش می‌شود، اما اندیس‌گذاری روی QuerySet ارزیابی‌نشده هر بار کوئری تازه می‌زند (بخش کش QuerySet).

۲. تفاوت select_related و prefetch_related چیست؟

select_related با JOIN و در همان کوئری اصلی، رابطه‌های تک‌مقداری (ForeignKey و OneToOne) را می‌آورد. prefetch_related برای چندبه‌چند و رابطهٔ معکوس است: کوئری جداگانه می‌زند و «اتصال» را در پایتون انجام می‌دهد. هر دو جلوی الگویی را می‌گیرند که اغلب «N+1» نامیده می‌شود: یک کوئری اصلی و بعد یک کوئری جدا برای هر شیء (اصطلاحی رایج میان توسعه‌دهندگان، نه عبارتی از مستندات).

Book.objects.select_related("author")      # JOIN, one query
Author.objects.prefetch_related("books")   # reverse FK, two queries

۳. F() و Q() را کجا به کار می‌برید؟

F() به مقدار یک فیلد در خود پایگاه داده اشاره می‌کند؛ برای مقایسهٔ دو فیلد یا افزایش یک شمارنده بدون خواندن مقدار در پایتون، و از race condition هم جلوگیری می‌کند. Q() برای شرط‌های ترکیبی است: OR با |، AND با & و NOT با ~.

۴. آیا update() و bulk_create() متد save() و سیگنال‌ها را صدا می‌زنند؟

خیر. update() نه save() را صدا می‌زند و نه سیگنال‌های pre_save و post_save را فعال می‌کند؛ bulk_create() هم همین‌طور است. پس منطقی که در save() یا سیگنال نوشته‌اید برای این عملیات گروهی اجرا نمی‌شود.

۵. transaction.atomic چه می‌کند و on_commit به چه درد می‌خورد؟

Django به‌صورت پیش‌فرض در حالت autocommit است و هر کوئری فوراً ثبت می‌شود. بلوک atomic یا کامل ثبت می‌شود یا کامل برمی‌گردد، و بلوک‌های تودرتو savepoint می‌سازند. on_commit تابعی را فقط بعد از ثبت موفق اجرا می‌کند، مثلاً برای شروع کاری پس‌زمینه که نباید پیش از ذخیرهٔ داده اجرا شود.

with transaction.atomic():
    order.save()
    transaction.on_commit(lambda: send_receipt(order.pk))

مدل‌ها و مایگریشن

۶. تفاوت null=True و blank=True چیست؟

null به پایگاه داده مربوط است (ذخیرهٔ NULL) و blank به اعتبارسنجی فرم (مجاز بودن مقدار خالی). مستندات توصیه می‌کند روی CharField و TextField از null=True پرهیز کنید و رشتهٔ خالی را حالت «بدون داده» بدانید؛ استثنا وقتی است که همزمان unique=True و blank=True لازم دارید.

۷. مهم‌ترین گزینه‌های on_delete در ForeignKey کدام‌اند؟

CASCADE رکوردهای وابسته را هم حذف می‌کند و PROTECT با ProtectedError جلوی حذف را می‌گیرد. RESTRICT با RestrictedError همین کار را می‌کند، اما اگر شیء مرجع در همان عملیات از طریق رابطهٔ CASCADE دیگری هم حذف شود، اجازه می‌دهد. SET_NULL فیلد را NULL می‌کند (نیازمند null=True) و SET_DEFAULT مقدار پیش‌فرض را می‌گذارد. انتخاب را قاعدهٔ کسب‌وکار تعیین می‌کند، نه عادت.

۸. makemigrations با migrate چه فرقی دارد و مایگریشن داده چیست؟

makemigrations از تغییر مدل‌ها فایل مایگریشن می‌سازد و migrate آن را اعمال یا برگشت می‌دهد. فایل‌های مایگریشن بخشی از کد هستند و باید در مخزن بمانند. تغییر دادهٔ موجود با مایگریشن داده و RunPython انجام می‌شود؛ Django آن را خودکار نمی‌سازد و بهتر است جدا از مایگریشن ساختار نوشته شود (مستندات مایگریشن).

ویو و URL

۹. ویوی تابعی بهتر است یا کلاسی؟

ویوی کلاسی جایگزین ویوی تابعی نیست، راه دیگری است: به‌جای شرط‌گذاری روی request.method، هر متد HTTP تابع جداگانه‌ای در کلاس دارد و با mixin می‌توان کد مشترک را بازاستفاده کرد. برای منطق ساده، یک تابع کوتاه کافی است.

۱۰. Django چطور از URL به ویو می‌رسد و چرا باید URL‌ها را نام‌گذاری کرد؟

از ROOT_URLCONF شروع می‌کند، urlpatterns را به ترتیب مرور می‌کند و در اولین تطبیق می‌ایستد. با name می‌توانید با reverse() یا {% url %} آدرس بسازید تا تغییر مسیر، لینک‌های هاردکدشده را نشکند.

۱۱. ترتیب MIDDLEWARE مهم است؟

بله. درخواست از بالا به پایین لایه‌ها می‌رسد و پاسخ از پایین به بالا برمی‌گردد، مثل پیاز. هر middleware می‌تواند بدون صدا زدن get_response خودش پاسخ بدهد و لایه‌های داخلی‌تر، از جمله خود ویو، را دور بزند.

قالب و فرم

۱۲. ارث‌بری قالب‌ها چگونه کار می‌کند؟

قالب فرزند با {% extends "base.html" %} شروع می‌شود (باید اولین تگ قالب باشد) و فقط بلوک‌هایی (block) را که لازم دارد بازنویسی می‌کند. محتوای هر بلوک در قالب والد به‌عنوان مقدار پیش‌فرض عمل می‌کند و با {{ block.super }} می‌توان آن را در قالب فرزند هم نمایش داد.

۱۳. Form با ModelForm چه فرقی دارد و چرا fields = "__all__" توصیه نمی‌شود؟

ModelForm فیلدهای فرم را از مدل می‌سازد و تعریف تکراری را حذف می‌کند. مستندات قویاً توصیه می‌کند فیلدهای قابل‌ویرایش را صریح بنویسید، چون __all__ یا exclude می‌تواند ناخواسته اجازهٔ تغییر فیلدی حساس را به کاربر بدهد.

۱۴. ترتیب اعتبارسنجی فرم چیست؟

هر فیلد به ترتیب to_python()، validate() و validator‌هایش را اجرا می‌کند؛ بعد متد clean_fieldname() (به‌جای fieldname، نام همان فیلد) برای همان فیلد می‌آید و در پایان clean() فرم، برای قاعده‌هایی که چند فیلد را با هم می‌سنجند. خطاها با ValidationError اعلام می‌شوند و cleaned_data فقط داده‌های معتبر را دارد.

امنیت

۱۵. حفاظت CSRF چگونه کار می‌کند؟

کوکی CSRF یک راز تصادفی دارد؛ تگ {% csrf_token %} در هر فرم POST فیلد پنهان csrfmiddlewaretoken را با نسخهٔ ماسک‌شدهٔ همان راز می‌سازد و میان‌افزار برای درخواست‌های غیرایمن راز فیلد را با کوکی مقایسه می‌کند (سرایند Origin را هم بررسی می‌کند). متدهای «امن» مثل GET، HEAD، OPTIONS و TRACE بررسی نمی‌شوند، پس عملیات تغییردهنده را با GET انجام ندهید و از csrf_exempt تا جای ممکن پرهیز کنید.

۱۶. Django در برابر SQL injection چه می‌کند و خطر کجا برمی‌گردد؟

QuerySet‌ها کوئری را پارامتری می‌سازند: کد SQL جدا از پارامترها تعریف می‌شود و درایور پایگاه داده پارامترها را escape می‌کند. خطر وقتی برمی‌گردد که برای raw() یا cursor.execute() ورودی کاربر را با رشته‌سازی در SQL بچسبانید؛ پارامترها را جدا بدهید و placeholder را داخل کوتیشن نگذارید. روی extra() و RawSQL هم احتیاط لازم است (امنیت در Django).

Person.objects.raw("SELECT * FROM myapp_person WHERE last_name = %s", [lname])

۱۷. XSS چیست و auto-escaping چطور کمک می‌کند؟

XSS یعنی اجرای اسکریپت مهاجم در مرورگر کاربری دیگر. قالب‌های Django خروجی هر متغیر را به‌صورت پیش‌فرض escape می‌کنند؛ مثلاً علامت کوچک‌تر را به معادل امن آن در HTML تبدیل می‌کنند تا مرورگر آن را تگ نخواند. خطر از جایی می‌آید که این محافظت را خاموش کنید: فیلتر safe، mark_safe، {% autoescape off %} یا نمایش HTML خام ذخیره‌شده در پایگاه داده؛ این‌ها را فقط برای محتوای مطمئن به کار ببرید.

احراز هویت و نشست

۱۸. authenticate() و login() چه فرقی دارند؟

authenticate() فقط اعتبار ورودی را با backend‌های احراز هویت می‌سنجد و کاربر یا None برمی‌گرداند. login() شناسهٔ کاربر را در نشست ذخیره می‌کند و کلید نشست را هم عوض می‌کند (cycle_key) تا حملهٔ session fixation سخت‌تر شود.

۱۹. login_required و مجوزها چطور کار می‌کنند؟

login_required (یا LoginRequiredMixin در ویوی کلاسی) کاربر ناشناس را به LOGIN_URL می‌فرستد؛ mixin باید در وراثت از همه چپ‌تر باشد. برای هر مدل چهار مجوز پیش‌فرض (add، change، delete، view) ساخته می‌شود که با has_perm() یا permission_required بررسی می‌شوند و گروه‌ها آن‌ها را میان چند کاربر مشترک می‌کنند.

۲۰. مدل کاربر سفارشی را کِی و چطور تعریف می‌کنند؟

مستندات برای پروژهٔ جدید نشان می‌دهد چطور با زیرکلاس AbstractUser مدل کاربر سفارشی بسازید و پیش از اولین migrate مقدار AUTH_USER_MODEL را به آن اشاره دهید. تغییر آن بعد از ساخته شدن جدول‌ها ممکن است، اما طبق مستندات می‌تواند پیچیده باشد و خودکار انجام نمی‌شود؛ برای همین در عمل معمولاً از روز اول مدل سفارشی می‌سازند (رویهٔ رایج، نه توصیهٔ صریح مستندات).

۲۱. نشست‌ها کجا ذخیره می‌شوند؟

پیش‌فرض، بک‌اند پایگاه داده است (جدول django_session) و کوکی فقط شناسهٔ نشست را نگه می‌دارد. گزینه‌های دیگر cached_db، کش‌محور، فایل و کوکی امضاشده است. در کوکی امضاشده داده رمزگذاری نمی‌شود و فقط امضا دارد، پس چیز محرمانه‌ای در آن نگذارید.

کارایی و کش

۲۲. قبل از بهینه‌سازی چطور می‌فهمید کجا کند است؟

اول اندازه‌گیری کنید، حدس نزنید. مستندات کارایی Django ابزار شخص ثالث django-debug-toolbar را معرفی می‌کند که کوئری‌های هر صفحه و زمان هرکدام را نشان می‌دهد. وقتی گلوگاه پیدا شد، اول کوئری‌ها و الگوی N+1 را بررسی کنید؛ همان مستندات تأکید می‌کند کش جایگزین اصلاح کدِ بد نیست.

۲۳. چرا count() یا exists() بهتر از len() روی QuerySet است؟

کار را تا جای ممکن به پایگاه داده بسپارید: count() از SELECT COUNT(*) استفاده می‌کند و exists() فقط وجود نتیجه را می‌پرسد، در حالی که len(qs) همهٔ سطرها را در حافظهٔ پایتون بارگیری می‌کند.

۲۴. سطح‌های کش در Django و بک‌اندهای آن کدام‌اند؟

از کش کل سایت و کش هر ویو با @cache_page تا کش تکه‌ای قالب با {% cache %} و API سطح پایین (cache.get و cache.set). بک‌اندهای داخلی شامل Memcached، Redis، پایگاه داده، فایل و حافظهٔ محلی است؛ حافظهٔ محلی پیش‌فرض است، اما هر پردازه کش جداگانه دارد و برای محیط تولید (production) مناسب نیست.

تست

۲۵. TestCase، SimpleTestCase و TransactionTestCase چه فرقی دارند؟

TestCase هر تست را در تراکنشی اجرا و در پایان rollback می‌کند؛ برای تست‌های دارای پایگاه داده پرکاربردترین گزینه است و چون truncate نمی‌کند، از TransactionTestCase سریع‌تر است. TransactionTestCase بعد از هر تست جدول‌ها را truncate می‌کند و برای تست رفتار commit و rollback لازم است. SimpleTestCase تراکنش ندارد و به‌طور پیش‌فرض اجازهٔ کوئری نمی‌دهد.

۲۶. با Test Client چه چیزی را آزمون می‌کنید؟

self.client چرخهٔ کامل درخواست و پاسخ را شبیه‌سازی می‌کند: get، post، login یا force_login و بررسی با assertContains و assertRedirects. assertNumQueries هم تعداد کوئری‌های یک بلوک را ثابت نگه می‌دارد و کمک می‌کند N+1 بی‌صدا برنگردد.

def test_list_view_queries(self):
    with self.assertNumQueries(2):
        self.client.get("/books/")

۲۷. Django برای تست‌ها با پایگاه داده چه می‌کند؟

پایگاه داده‌ای جداگانه می‌سازد (نامش با پیشوند test_ شروع می‌شود و SQLite به‌طور پیش‌فرض در حافظه کار می‌کند) و بعد از تست‌ها پاکش می‌کند، پس به داده‌های واقعی دست نمی‌زند. گزینهٔ --keepdb آن را نگه می‌دارد و --parallel تست‌ها را موازی اجرا می‌کند.

استقرار و تنظیمات

۲۸. پیش از استقرار کدام تنظیمات را حتماً بازبینی می‌کنید؟

DEBUG باید False باشد (در حالت روشن، صفحهٔ خطا بخش‌هایی از کد، متغیرهای محلی و تنظیمات را نمایش می‌دهد)، SECRET_KEY بزرگ و تصادفی و بیرون از مخزن باشد، و ALLOWED_HOSTS صریح تنظیم شود. HTTPS را اجباری کنید (ریدایرکت را وب‌سرور یا SECURE_SSL_REDIRECT انجام می‌دهد؛ پشت پراکسی به SECURE_PROXY_SSL_HEADER دقت کنید) و SESSION_COOKIE_SECURE و CSRF_COOKIE_SECURE را فعال کنید. python manage.py check --deploy بخشی از این کنترل‌ها را خودکار می‌کند؛ فهرست کامل در چک‌لیست استقرار هست.

۲۹. فایل‌های استاتیک در محیط تولید چطور سرو می‌شوند؟

با DEBUG=True و برنامهٔ django.contrib.staticfiles، سرور توسعه فایل‌های استاتیک را خودکار می‌دهد، اما در محیط تولید باید STATIC_ROOT را تعریف کنید، دستور collectstatic را اجرا کنید، و وب‌سرور یا یک سرویس فایل استاتیک آن‌ها را روی STATIC_URL ارائه کند.

۳۰. WSGI و ASGI چه فرقی دارند و چرا runserver برای محیط تولید نیست؟

WSGI استاندارد اصلی ارتباط وب‌سرور و برنامهٔ پایتونی است اما فقط کد همزمان (synchronous) را پشتیبانی می‌کند؛ ASGI استاندارد جدیدتر و سازگار با قابلیت‌های ناهمگام است. runserver فقط یک سرور سبک برای توسعه است و برای محیط تولید مناسب نیست.

حفظ کردن پاسخ‌ها جای ساختن پروژه را نمی‌گیرد

این فهرست برای مرور سریع است، نه جایگزین تمرین. در مصاحبه ممکن است پرسش پیگیری مثل «کجا این را دیده‌اید؟» یا «اگر خراب شود چه می‌شود؟» بیاید و پاسخ حفظی همان‌جا کم بیاورد. یک راه مؤثر برای آماده شدن به سوالات مصاحبه Django این است که پروژهٔ کوچکی بسازید و عمداً با N+1، مایگریشن داده، تنظیمات استقرار و تست روبه‌رو شوید.

چک‌لیست مرور پیش از مصاحبه

  • یک بار N+1 را عمداً بسازید و با select_related یا prefetch_related رفعش کنید.
  • یک مایگریشن داده با RunPython بنویسید، برایش reverse_code (حتی RunPython.noop) تعریف کنید و هم اجرا و هم برگشتش را امتحان کنید.
  • برای یک ویو، تستی با Test Client و assertNumQueries بنویسید.
  • پروژه‌تان را با check --deploy اجرا کنید و هشدارها را بخوانید.
  • فرق نسخهٔ مورد استفاده‌تان با Django 5.2 LTS را در مستندات رسمی مرور کنید.

برای دیدن حوزه‌هایی که ارزیابی عملی Python/Django پوشش می‌دهد (Python، حل مسئله، پایگاه داده، تست، امنیت و استقرار Django)، صفحهٔ معرفی آن را ببینید. اگر خودتان به‌دنبال ساخت وب‌اپلیکیشن یا MVP با Python و Django هستید، ساخت وب‌اپلیکیشن و MVP اختصاصی و صفحهٔ تماس را ببینید.