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

جنگو چیست و چرا از آن استفاده کنیم؟

اگر با مفاهیم پایه‌ی پایتون آشنا نیستید، اول آموزش پایتون از صفر را بخوانید. اما فرض کنیم پایتون را می‌شناسید و به‌دنبال ساختن یک اپلیکیشن وب هستید. جنگو یک چارچوب (Framework) وب است که فلسفه‌ی «باتری‌ها همه نصب‌اند» را دنبال می‌کند: یعنی به‌جای این‌که شما برای هر چیز کوچکی کتابخانه‌ی جدا نصب کنید، جنگو با ابزارهای آماده می‌آید — از ORM و پنل مدیریت تا سیستم احراز هویت و مهاجرت دیتابیس.

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

سه دلیل اصلی که در پروژه‌های واقعی، جنگو را انتخاب می‌کنم:

  • پروژه‌های داده‌محور: سیستم‌های مدیریت محتوا، سامانه‌های CRM، داشبوردهای داخلی. جایی که تعداد مدل‌ها زیاد است و نیاز به پنل مدیریت سریع، بیشتر از ظاهر سفارشی است.
  • پروژه‌هایی با نیاز به احراز هویت پیچیده: جنگو از ابتدا سیستم کاربر، نقش، دسترسی و رمزنگاری امن را دارد.
  • پروژه‌های مقیاس‌پذیر: اینستاگرام، Pinterest و Mozilla از جنگو استفاده کرده‌اند. الگوهای معماری جنگو، رشد را از روز اول پیش‌بینی می‌کند.
جنگو یک چارچوب نظرمحور است؛ اگر با فلسفه‌ی «باتری‌ها همه نصب‌اند» راحت نیستید، فلاسک انتخاب بهتری است.

جنگو یا فلاسک؛ انتخاب درست برای پروژه

این سوال را در مشاوره‌ها زیاد می‌شنوم و جوابم صادقانه این است: برای پروژه‌ی بزرگ، جنگو؛ برای API کوچک یا ابزار داخلی، فلاسک. تفصیل این انتخاب را در آموزش فلاسک در پایتون از منظر فلاسک باز کرده‌ام، ولی اینجا خلاصه‌ی معیارها را می‌گویم:

معیارجنگوفلاسک
سرعت راه‌اندازی اولیهکندترسریع‌تر
ابزارهای آمادهبسیار زیادحداقلی
ساختار تحمیلیمحکمآزاد
پنل مدیریت پیش‌فرضداردندارد
مناسب برایاپلیکیشن داده‌محور، پلتفرمAPI، میکروسرویس

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

نصب جنگو و اولین پروژه

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

python -m venv venv
source venv/bin/activate        # در ویندوز: venv\Scripts\activate

pip install django
pip freeze > requirements.txt

django-admin startproject mysite
cd mysite
python manage.py runserver

با اجرای runserver، یک سرور توسعه روی http://127.0.0.1:8000/ بالا می‌آید. اگر صفحه‌ی خوش‌آمد جنگو را دیدید، همه‌چیز درست است. نکته‌ی مهمی که در پروژه‌های واقعی به آن تأکید می‌کنم: سرور توسعه‌ی جنگو هرگز برای محیط تولید نیست. برای محیط تولید، باید از Gunicorn یا uWSGI پشت Nginx یا Apache استفاده کنید — همین تفکیک ساده، بارها جلوی مشکلات امنیتی و کارایی را گرفته است.

ساختار پروژه‌ی جنگو را بشناسید

بعد از startproject، ساختار پوشه‌ی پروژه به این شکل است:

mysite/
├── manage.py
└── mysite/
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    ├── asgi.py
    └── wsgi.py

سه فایل مهم که بیشترین زمان را با آن‌ها می‌گذرانم:

  • settings.py: تنظیمات پروژه، از دیتابیس و اپ‌های نصب‌شده تا تنظیمات امنیتی. این فایل، قلب پروژه است.
  • urls.py: نقشه‌ی مسیرها. هر URL به یک ویو وصل می‌شود.
  • manage.py: ابزار خط فرمان. از این به بعد، اکثر دستورات را با python manage.py ... اجرا می‌کنید.

یک نکته‌ی مهم درباره‌ی settings.py که در پروژه‌های واقعی حیاتی است: هرگز کلیدهای حساس (مثل SECRET_KEY و رمز دیتابیس) را مستقیم در فایل ننویسید. از متغیرهای محیطی یا فایل‌های .env استفاده کنید. اگر با مفهوم امنیت در PHP آشنا هستید، امنیت در PHP همان اصول را با سینتکس متفاوتی توضیح می‌دهد و مستقیماً به جنگو هم قابل انتقال است.

ساخت اولین اپ و مدل داده

در جنگو، هر بخش کاربردی پروژه در یک «اپ» (App) جدا قرار می‌گیرد. مثلاً یک اپ برای وبلاگ، یک اپ برای فروشگاه. برای ساخت اپ:

python manage.py startapp blog

حالا اپ blog را در INSTALLED_APPS در فایل settings.py اضافه کنید. در ادامه، اولین مدل داده را می‌سازیم:

# blog/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

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

تفاوت مهمی که در پروژه‌های واقعی محسوس است: مدل‌های جنگو، ORM (Object-Relational Mapping) هستند، یعنی شما به‌جای نوشتن SQL، با اشیای پایتونی کار می‌کنید. این ویژگی، هم سرعت توسعه را بالا می‌برد و هم امنیت را — چون جنگو به‌طور خودکار جلوی SQL Injection را می‌گیرد. اما در پروژه‌های پیچیده، همین ORM می‌تواند به گلوگاه تبدیل شود، چون نادیده‌گرفتن رفتار کوئری‌های تولیدشده، به کندی خاموش می‌انجامد — همان درسی که در بهینه‌سازی کوئری‌های وردپرس با کدنویسی هم معادلش را در بستر PHP دیده‌ام.

مهاجرت‌ها: پایگاه داده از روی مدل

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

python manage.py makemigrations
python manage.py migrate

دستور اول، فایل مهاجرت را از روی مدل‌ها می‌سازد؛ دستور دوم، آن را روی دیتابیس اعمال می‌کند. در پروژه‌های واقعی، این چرخه بارها تکرار می‌شود. یک نکته‌ی مهم: در پروژه‌های تیمی، همیشه فایل‌های مهاجرت را در گیت نگه دارید. حذف یا بازنویسی مهاجرت‌های قدیمی، یکی از بدترین عادت‌هایی است که در پروژه‌های واقعی دیده‌ام — چون باعث می‌شود وضعیت دیتابیس روی ماشین‌های مختلف متفاوت شود.

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

پنل مدیریت؛ گنجینه‌ی پنهان جنگو

شاید بزرگ‌ترین مزیت جنگو نسبت به چارچوب‌های دیگر، پنل مدیریت آماده‌ی آن است. برای فعال‌سازی، اول یک کاربر مدیر بسازید:

python manage.py createsuperuser

سپس مدل‌تان را در پنل مدیریت ثبت کنید:

# blog/admin.py
from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "slug", "created_at")
    search_fields = ("title", "body")
    prepopulated_fields = {"slug": ("title",)}

حالا با ورود به /admin/، یک پنل مدیریت کامل با جستجو، فیلتر و امکان ویرایش مدل‌ها در اختیار دارید. در پروژه‌های واقعی، همین پنل، هفته‌ها زمان ساخت یک CRUD اختصاصی را ذخیره می‌کند. تجربه‌ی من این است که حتی اگر بعداً یک پنل سفارشی برای مشتری بسازید، پنل مدیریت پیش‌فرض جنگو، ابزار ارزشمندی برای تیم فنی باقی می‌ماند.

ویو و URL؛ منطق پشت صفحه

ویو در جنگو، تابعی است که یک درخواست HTTP می‌گیرد و یک پاسخ HTTP برمی‌گرداند. ساده‌ترین شکل آن:

# blog/views.py
from django.shortcuts import render
from .models import Post

def post_list(request):
    posts = Post.objects.all().order_by("-created_at")
    return render(request, "blog/post_list.html", {"posts": posts})

def post_detail(request, slug):
    post = Post.objects.get(slug=slug)
    return render(request, "blog/post_detail.html", {"post": post})

و در فایل URL:

# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.post_list, name="post_list"),
    path("<slug:slug>/", views.post_detail, name="post_detail"),
]

یک نکته‌ی مهم در پروژه‌های واقعی: خطای Post.objects.get(slug=slug) اگر رکوردی پیدا نکند، DoesNotExist پرتاب می‌کند که نتیجه‌اش خطای ۵۰۰ است، درحالی‌که باید ۴۰۴ باشد. راه درست استفاده از get_object_or_404 است:

from django.shortcuts import get_object_or_404

post = get_object_or_404(Post, slug=slug)

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

قالب‌ها؛ لایه‌ی نمایش

قالب‌های جنگو شبیه HTML با متغیرهای ویژه هستند. یک نمونه ساده:

<!-- blog/templates/blog/post_list.html -->
<h1>Blog Posts</h1>

<ul>
  {% for post in posts %}
    <li>
      <a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a>
      <small>{{ post.created_at|date:"Y-m-d" }}</small>
    </li>
  {% empty %}
    <li>No posts yet.</li>
  {% endfor %}
</ul>

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

فرم‌ها و اعتبارسنجی خودکار

جنگو سیستم فرم‌های قدرتمندی دارد که همزمان سه کار انجام می‌دهد: نمایش HTML، اعتبارسنجی، و پاک‌سازی داده. نمونه:

# blog/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "slug", "body"]

و در ویو:

def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("post_list")
    else:
        form = PostForm()

    return render(request, "blog/post_form.html", {"form": form})

همین چند خط کد، در پشت صحنه، جلوی CSRF (Cross-Site Request Forgery)، XSS و تزریق SQL را می‌گیرد. اگر با این مفاهیم در بستر دیگری آشنا هستید، امنیت در PHP همان اصول را با جزئیات بیشتری توضیح می‌دهد — تفاوت این‌جاست که در جنگو، این محافظت‌ها از پیش فعال‌اند و شما فقط از آن‌ها بهره می‌برید.

در جنگو، اکثر محافظت‌های امنیتی از پیش فعال‌اند؛ کاری که باید بکنید، این است که آن‌ها را غیرفعال نکنید.

سیستم احراز هویت آماده

جنگو از ابتدا با سیستم کاربر همراه است. برای ورود و خروج، حتی نیازی به نوشتن ویو ندارید:

# urls.py
from django.contrib.auth import views as auth_views

urlpatterns = [
    path("login/",  auth_views.LoginView.as_view(),  name="login"),
    path("logout/", auth_views.LogoutView.as_view(), name="logout"),
]

و برای محافظت از یک ویو:

from django.contrib.auth.decorators import login_required

@login_required
def post_create(request):
    ...

در پروژه‌های واقعی، همین دو الگو، نیمی از کارهای مربوط به احراز هویت را حل می‌کند. مسائل پیشرفته‌تر مثل نقش‌ها و دسترسی‌های سفارشی، با Permission و Groupهای جنگو حل می‌شوند. اگر با تفاوت احراز هویت و مجوزدهی آشنا نیستید، تفاوت احراز هویت و مجوزدهی مفاهیم را روشن می‌کند — این تفکیک در جنگو، در دو لایه‌ی کاملاً مجزا پیاده‌سازی شده است.

اشتباهاتی که در پروژه‌های واقعی دیده‌ام

در بازبینی پروژه‌های جنگویی، این اشتباهات را زیاد دیده‌ام و هر کدام، یک درس عملی است:

  • نگه‌داشتن DEBUG=True در تولید: این تنظیم، جزئیات داخلی پروژه را در صفحه‌ی خطا نمایش می‌دهد و می‌تواند به مهاجم اطلاعات ارزشمند بدهد. در محیط تولید، DEBUG=False باشد.
  • عدم تنظیم ALLOWED_HOSTS: حتی با DEBUG=False، اگر ALLOWED_HOSTS تنظیم نشده باشد، جنگو خطا می‌دهد. دامنه‌ها را دقیق لیست کنید.
  • نادیده گرفتن ایندکس دیتابیس: مدل‌هایی که روی فیلدهای foreign key یا فیلدهای جستجوی مکرر ایندکس ندارند، در پروژه‌های پربازدید به گلوگاه تبدیل می‌شوند.
  • استفاده از Post.objects.all() در ویوهای بزرگ: همیشه تعداد رکوردهای برگشتی را با pagination یا filter محدود کنید. نمونه‌ی معادلش در وردپرس را در بهینه‌سازی کوئری‌های وردپرس با کدنویسی توضیح داده‌ام.
  • نوشتن منطق کسب‌وکار در قالب‌ها: زبان قالب جنگو، عمداً محدود است. تلاش برای فشردن منطق در آن، خوانایی و کارایی را به‌شدت پایین می‌آورد.
  • نادیده گرفتن select_related و prefetch_related: وقتی با مدل‌های مرتبط کار می‌کنید، این دو، جلوی مشکل کلاسیک N+1 query را می‌گیرند — همان چیزی که در وردپرس معادلش را در تأثیر دیتابیس بر سرعت سایت دیده‌ام.
  • نداشتن تست خودکار: پروژه‌ی جنگویی که تست ندارد، پس از چند ماه به جایی می‌رسد که هر تغییر، ریسک شکستن چند جای دیگر را دارد. تست نویسی را از همان هفته‌ی اول جدی بگیرید.

یک توصیه‌ی عملی که در پروژه‌های واقعی به‌کارم آمده: settings.py را به دو یا سه فایل تقسیم کنید — base.py، dev.py، production.py. این جداسازی، جلوی اشتباه‌های رایجی مثل DEBUG=True روی تولید را می‌گیرد. اگر با گیت در پروژه‌های وردپرسی آشنا هستید، همان اصول نسخه‌بندی و جداسازی تنظیمات بر اساس محیط، در جنگو هم مستقیم قابل استفاده است.

سخن آخر

جنگو، برای کسی که از یک اسکریپت ساده‌ی پایتونی عبور کرده و به ساخت اپلیکیشن وب رسیده، یک جهش محسوب می‌شود. سه نکته‌ی مهم که در این مقاله به آن‌ها رسیدیم: اول، انتخاب بین جنگو و فلاسک، قبل از هر چیز به اندازه و ماهیت پروژه برمی‌گردد — ابزار درست در جای درست، ارزشش را دارد؛ دوم، مزیت واقعی جنگو در ابزارهای آماده‌اش نهفته است — پنل مدیریت، ORM و سیستم احراز هویت، ماه‌ها کار را ذخیره می‌کنند؛ سوم، همان مقدار که این ابزارها کار را آسان می‌کنند، اشتباه در استفاده از آن‌ها هم هزینه دارد — از DEBUG=True در تولید تا نادیده گرفتن select_related، هر خطا در پروژه‌های بزرگ می‌تواند گران تمام شود.

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