Python Django Fullstack expliqué simplement (avec schémas et vrai code)
Python Django Fullstack : l'essentiel en un article — vrai code, schémas et étapes concrètes, extraits d'un cours de 24 leçons.
Un guide qui va droit au but : Python Django Fullstack décortiqué avec des schémas, des exemples concrets et des commandes testées. Tout vient d'un cours structuré de 8 chapitres — en voici le meilleur.
tl;dr
- Pourquoi Django
- Models et migrations
- Views et URLs
- Templates et forms
- Admin
~$ cat ./parcours.md # Python Django Fullstack — 8 chapitres
01
Pourquoi Django
→ Django vs Flask vs FastAPI→ Installation et premier projet+ 1 autres leçons
02
Models et migrations
→ Models et fields→ Relations+ 1 autres leçons
03
Views et URLs
→ Function-based views→ Class-based views (CBV)+ 1 autres leçons
04
Templates et forms
→ Template language→ Forms et ModelForms+ 1 autres leçons
05
Admin
→ Customizer ModelAdmin→ Actions et filters custom+ 1 autres leçons
06
Authentification
→ User model et auth views→ Custom User et Profile+ 1 autres leçons
07
Déploiement
→ Settings prod/dev→ Gunicorn + Nginx+ 1 autres leçons
08
Projet final blog ecom
→ Chapitre 07 – Projet final : Modeles et admin→ Chapitre 07 – Projet final : Views et templates+ 1 autres leçons
🏁
Projet final
→ Tu repars avec un projet concret et démontrable
Migrations
Cycle de base
# 1. Modifier un model class Article(models.Model): title = models.CharField(max_length=200) summary = models.TextField(blank=True) # NOUVEAU # 2. Generer la migration python manage.py makemigrations # Migrations for 'blog': # blog/migrations/0002_article_summary.py # 3. Appliquer python manage.py migrate # 4. Verifier python manage.py showmigrations
Voir le SQL genere
python manage.py sqlmigrate blog 0002 # Output : # BEGIN; # ALTER TABLE blog_article ADD COLUMN summary TEXT NOT NULL DEFAULT ''; # COMMIT;
Reverter une migration
python manage.py migrate blog 0001 # Revert vers 0001 (annule 0002) python manage.py migrate blog zero # Tout annuler pour l'app blog
Migration de donnees
python manage.py makemigrations --empty --name backfill_slugs blog # Editer le fichier genere : 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)]
Squashing migrations
# Apres beaucoup de migrations, les compacter : python manage.py squashmigrations blog 0001 0020 # Cree 0001_squashed_0020 qui remplace les 20 fichiers # Les nouveaux developpeurs evitent de jouer 20 migrations
Migrations sans downtime
NOTEPour ajouter une colonne NOT NULL sans casser la prod :
- Deploy 1 : Ajouter colonne nullable
- Deploy 2 : Backfill via migration data
- Deploy 3 : Modifier le code pour utiliser/setter
- Deploy 4 : Migration ALTER NOT NULL
Renommer un model
# Si tu renommes Article -> Post, Django va proposer # de creer un nouveau model + supprimer l'ancien (perd les donnees !) # Solution : editer la migration generee operations = [ migrations.RenameModel("Article", "Post") ] # Idem pour les champs operations = [ migrations.RenameField("Post", "summary", "excerpt") ]
Django vs Flask vs FastAPI
NOTEObjectif — Comprendre la philosophie Django et quand le choisir.
Comparaison rapide
| Critere | Django | Flask | FastAPI |
|---|---|---|---|
| Philosophie | Batteries included | Micro framework | API first, async |
| ORM | Integre | SQLAlchemy externe | SQLAlchemy externe |
| Admin | Auto-genere | A coder | A coder |
| Auth | Complete | Extensions | A coder JWT |
| Templates | Django templates | Jinja2 | Jinja2 optionnel |
| Async natif | Partiel (4.x) | Non | Oui |
| Apprentissage | Plus long | Tres simple | Moyen |
Quand choisir Django
Quand NE PAS choisir Django
Architecture MTV
+----------+ request +------+ query +-------+
| Browser | -----------> | View | --------> | Model |
+----------+ +------+ +-------+
^ | |
| v v
| +----------+ +-------+
+----------------- | Template |<--------| DB |
+----------+ +-------+
# M = Model (ORM, base de donnees)
# T = Template (HTML)
# V = View (logique controle, en MVC c'est le Controller)Ecosysteme Django
Versions et LTS
NOTEDjango releases :
- Django 5.0+ : actuelle
- Django 4.2 LTS : supportee jusqu'a 2026
- Cycle : nouvelle version tous les 8 mois
- LTS tous les 2 ans, supportees 3 ans
Companies qui utilisent Django
Resume
NOTEA retenir
- Django = batteries included pour apps web complexes
- Architecture MTV (Model, Template, View)
- Ideal : SaaS, CMS, e-commerce, dashboards
- API pure : prefere DRF ou FastAPI
Chapitre 07 – Projet final : Modeles et admin
Cahier des charges
NOTEObjectif — Construire une boutique en ligne minimaliste avec : catalogue, panier, commandes, admin metier.
Structure projet
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
Cet article couvre les extraits les plus utiles — le cours complet Python Django Fullstack (8 chapitres, 24 leçons, exercices corrigés et projet final) t'emmène jusqu'au bout.
./acceder-au-cours-complet cours gratuit : Vibe CodingFAQ
Combien de temps pour apprendre Python Django Fullstack ?
Avec une progression structurée (8 chapitres, 24 leçons courtes et pratiques), on atteint un niveau opérationnel en quelques semaines à raison de 30 à 60 minutes par jour. L'important est de pratiquer chaque notion immédiatement.
Faut-il des prérequis ?
Des bases en informatique suffisent. Si tu sais utiliser un terminal et lire du code simple, tu es prêt.
Par où commencer concrètement ?
Reproduis les commandes de cet article, puis suis le cours complet Python Django Fullstack : il enchaîne les 24 leçons dans l'ordre, avec exercices et projet final.
./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)📬 Tu veux recevoir ce type de guide chaque semaine ? Abonne-toi gratuitement — code réel, zéro blabla.