شرح بسيط لـ Python Django Fullstack (مع رسوم توضيحية وكود حقيقي)
Python Django Fullstack : الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة مكونة من 24 درسًا.
دليل مباشر إلى الهدف: Python Django Fullstack مفكك مع مخططات ورسوم بيانية، أمثلة عملية وأوامر مجربة. كل شيء من دورة منظمة من 8 فصول — إليك أفضلها.
tl;dr
- لماذا Django
- Models et migrations
- Views et URLs
- Templates et forms
- Admin
~$ cat ./parcours.md # Python Django Fullstack — 8 فصول
01
لماذا Django
→ Django vs Flask vs FastAPI→ Installation et premier projet+ 1 دروس أخرى
02
النماذج والهجرات
→ Models et fields→ Relations+ 1 دروس أخرى
03
العروض والروابط
→ Function-based views→ Class-based views (CBV)+ 1 دروس أخرى
04
القوالب والنماذج
→ Template language→ Forms et ModelForms+ 1 دروس أخرى
05
الإدارة
→ Customizer ModelAdmin→ Actions et filters custom+ 1 دروس أخرى
06
المصادقة
→ User model et auth views→ Custom User et Profile+ 1 دروس أخرى
07
النشر
→ Settings prod/dev→ Gunicorn + Nginx+ 1 دروس أخرى
08
المشروع النهائي مدونة إلكترونية
→ Chapitre 07 – Projet final : Modeles et admin→ Chapitre 07 – Projet final : Views et templates+ 1 دروس أخرى
🏁
المشروع النهائي
→ تعود بمشروع ملموس وقابل للعرض
التهجيرات
الدورة الأساسية
# 1. تعديل نموذج class Article(models.Model): title = models.CharField(max_length=200) summary = models.TextField(blank=True) # جديد # 2. توليد التهجيرة python manage.py makemigrations # Migrations for 'blog': # blog/migrations/0002_article_summary.py # 3. التطبيق python manage.py migrate # 4. التحقق python manage.py showmigrations
عرض الـ SQL المولد
python manage.py sqlmigrate blog 0002 # Output : # BEGIN; # ALTER TABLE blog_article ADD COLUMN summary TEXT NOT NULL DEFAULT ''; # COMMIT;
التراجع عن تهجيرة
python manage.py migrate blog 0001 # التراجع إلى 0001 (إلغاء 0002) python manage.py migrate blog zero # إلغاء كل شيء لتطبيق blog
تهجيرة البيانات
python manage.py makemigrations --empty --name backfill_slugs blog # تعديل الملف المولد: from django.db import migrations from django.utils.text import slugify def backfill_slugs(apps, schema_editor): Article = apps.get_model("blog", "Article") for a in Article.objects.all(): if not a.slug: a.slug = slugify(a.title) a.save() def reverse_func(apps, schema_editor): pass class Migration(migrations.Migration): dependencies = [("blog", "0001_initial")] operations = [migrations.RunPython(backfill_slugs, reverse_func)]
ضغط التهجيرات
# بعد الكثير من التهجيرات، ضغطها: python manage.py squashmigrations blog 0001 0020 # ينشئ 0001_squashed_0020 الذي يحل محل 20 ملفًا # يتجنب المطورون الجدد تشغيل 20 تهجيرة
تهجيرات بدون توقف
NOTEلإضافة عمود NOT NULL دون تعطيل الإنتاج:
- النشر 1: إضافة عمود قابل للقيم الفارغة
- النشر 2: تعبئة البيانات عبر تهجيرة بيانات
- النشر 3: تعديل الكود للاستخدام/الضبط
- النشر 4: تهجيرة ALTER NOT NULL
إعادة تسمية نموذج
# إذا أعدت تسمية Article -> Post، سيقترح Django # إنشاء نموذج جديد + حذف القديم (فقدان البيانات!) # الحل: تعديل التهجيرة المولدة operations = [ migrations.RenameModel("Article", "Post") ] # نفس الشيء للحقول operations = [ migrations.RenameField("Post", "summary", "excerpt") ]
Django vs Flask vs FastAPI
NOTEالهدف — فهم فلسفة Django ومتى تختاره.
مقارنة سريعة
| المعيار | Django | Flask | FastAPI |
|---|---|---|---|
| الفلسفة | Batteries included | Micro framework | API first, async |
| ORM | مدمج | SQLAlchemy خارجي | SQLAlchemy خارجي |
| Admin | مولد تلقائيًا | يُكتب يدويًا | يُكتب يدويًا |
| Auth | كامل | إضافات | كتابة JWT يدويًا |
| Templates | Django templates | Jinja2 | Jinja2 اختياري |
| Async أصلي | جزئي (4.x) | لا | نعم |
| التعلم | أطول | بسيط جدًا | متوسط |
متى تختار Django
متى لا تختار Django
هندسة MTV
+----------+ request +------+ query +-------+
| Browser | -----------> | View | --------> | Model |
+----------+ +------+ +-------+
^ | |
| v v
| +----------+ +-------+
+----------------- | Template |<--------| DB |
+----------+ +-------+
# M = Model (ORM، قاعدة البيانات)
# T = Template (HTML)
# V = View (منطق التحكم، في MVC هو الـ Controller)منظومة Django
الإصدارات وLTS
NOTEإصدارات Django:
- Django 5.0+ : الحالي
- Django 4.2 LTS : مدعوم حتى 2026
- الدورة: إصدار جديد كل 8 أشهر
- LTS كل سنتين، مدعومة 3 سنوات
الشركات التي تستخدم Django
الملخص
NOTEللتذكر
- Django = batteries included لتطبيقات الويب المعقدة
- هندسة MTV (Model, Template, View)
- مثالي: SaaS، CMS، التجارة الإلكترونية، لوحات التحكم
- API نقي: يفضل DRF أو FastAPI
الفصل 07 – المشروع النهائي: النماذج والإدارة
دفتر الشروط
NOTEالهدف — بناء متجر إلكتروني بسيط يحتوي على: كتالوج، سلة، طلبات، إدارة أعمال.
هيكل المشروع
myshop/
manage.py
myshop/
settings/
urls.py
wsgi.py
accounts/
models.py # User custom
views.py
urls.py
catalog/
models.py # Category, Product
views.py
admin.py
cart/
cart.py # classe Cart (session)
views.py
orders/
models.py # Order, OrderItem
views.py
admin.py
templates/
static/
media/accounts/models.py
from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): phone = models.CharField(max_length=20, blank=True) address = models.TextField(blank=True) city = models.CharField(max_length=100, blank=True) postal_code = models.CharField(max_length=10, blank=True) country = models.CharField(max_length=2, default="CA") class Meta: ordering = ["-date_joined"] def __str__(self): return self.username
catalog/models.py
from django.db import models from django.urls import reverse from django.utils.text import slugify class Category(models.Model): name = models.CharField(max_length=100, unique=True) slug = models.SlugField(max_length=120, unique=True) description = models.TextField(blank=True) parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE, related_name="children") class Meta: verbose_name_plural = "Categories" ordering = ["name"] def __str__(self): return self.name def get_absolute_url(self): return reverse("catalog:category", kwargs={"slug": self.slug}) class Product(models.Model): category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name="products") name = models.CharField(max_length=200) slug = models.SlugField(max_length=220, unique=True) description = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) image = models.ImageField(upload_to="products/%Y/%m/") stock = models.PositiveIntegerField(default=0) available = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["-created_at"] indexes = [ models.Index(fields=["slug"]), models.Index(fields=["available", "-created_at"]), ] def __str__(self): return self.name def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) def get_absolute_url(self): return reverse("catalog:product", kwargs={"slug": self.slug}) @property def in_stock(self): return self.available and self.stock > 0
orders/models.py
class Order(models.Model): class Status(models.TextChoices): PENDING = "pending", "En attente" PAID = "paid", "Paye" SHIPPED = "shipped", "Expedie" DELIVERED = "delivered", "Livre" CANCELED = "canceled", "Annule" user = models.ForeignKey("accounts.User", on_delete=models.PROTECT, related_name="orders") status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) address = models.TextField() city = models.CharField(max_length=100) postal_code = models.CharField(max_length=10) country = models.CharField(max_length=2) total = models.DecimalField(max_digits=12, decimal_places=2, default=0) paid_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["-created_at"] def __str__(self): return f"Order #{self.id}" class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items") product = models.ForeignKey("catalog.Product", on_delete=models.PROTECT) price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField(default=1) @property def subtotal(self): return self.price * self.quantity
catalog/admin.py
@admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ["name", "parent", "product_count"] prepopulated_fields = {"slug": ("name",)} search_fields = ["name"] def product_count(self, obj): return obj.products.count() @admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = ["name", "category", "price", "stock", "available"] list_filter = ["available", "category", "created_at"] list_editable = ["price", "stock", "available"] search_fields = ["name", "description"] prepopulated_fields = {"slug": ("name",)} list_per_page = 50
orders/admin.py
class OrderItemInline(admin.TabularInline): model = OrderItem extra = 0 readonly_fields = ["product", "price", "quantity", "subtotal"] can_delete = False @admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display = ["id", "user", "status", "total", "created_at", "paid_at"] list_filter = ["status", "created_at"] search_fields = ["id", "user__username", "user__email"] inlines = [OrderItemInline] actions = ["mark_shipped"] date_hierarchy = "created_at" @admin.action(description="Marquer comme expedie") def mark_shipped(self, request, queryset): queryset.update(status=Order.Status.SHIPPED)
va-plus-loin
يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة Python Django Fullstack (8 فصول، 24 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.
./acceder-au-cours-complet cours gratuit : Vibe Codingالأسئلة الشائعة
كم من الوقت يستغرق تعلم Python Django Fullstack؟
مع تقدم منظم (8 فصول، 24 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في بضعة أسابيع بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو تطبيق كل مفهوم فورًا.
هل هناك متطلبات سابقة؟
تكفي أساسيات الحاسوب. إذا كنت تعرف استخدام الطرفية وقراءة كود بسيط، فأنت جاهز.
من أين نبدأ عمليًا؟
طبّق أوامر هذا المقال، ثم تابع الدورة الكاملة Python Django Fullstack: تربط الـ24 درسًا بالترتيب مع تمارين ومشروع نهائي.
./a-lire-aussi
→ Lance-toi en Portfolio IA SEO Vercel : ton premier pas concret aujourd'hui→ IA Stripe GitHub SaaS en pratique : le code et les commandes qui comptent vraiment→ Python Requests APIs expliqué simplement (avec schémas et vrai code)📬 هل تريد تلقي هذا النوع من الأدلة كل أسبوع؟ اشترك مجانًا — كود حقيقي، بدون كلام زائد.