Cours Django Complet 🐍

Les 12 chapitres essentiels — MVT, ORM, templates, REST API et déploiement

12
Chapitres
100+
Exemples de code
Django 5+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et installation

🐍 Premier projet Django
# Créer un environnement virtuel
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows

# Installer Django
pip install django

# Créer un projet
django-admin startproject monsite .
# ^ le point crée dans le dossier courant

# Lancer le serveur
python manage.py runserver # → http://127.0.0.1:8000

# Structure :
# monsite/
# ├── manage.py (CLI Django)
# └── monsite/
# ├── __init__.py
# ├── settings.py (configuration)
# ├── urls.py (routing principal)
# ├── asgi.py
# └── wsgi.py (production)

Django est le framework web Python le plus populaire. Créé en 2005, il suit la philosophie « batteries included » : ORM, admin, auth, formulaires, tout est intégré. Utilisé par Instagram, Pinterest, Disqus, Mozilla et Spotify. Architecture MVT (Model-View-Template).

🏗️ Django en résumé
CaractéristiqueDjango
ArchitectureMVT (Model-View-Template)
ORMIntégré (puissant, migrations auto)
AdminInterface d'administration automatique
AuthSystème complet (login, register, permissions)
SécuritéCSRF, XSS, SQL injection — protégé par défaut
APIDjango REST Framework (DRF)
🧠 Quiz
Quelle est la différence entre MVC et MVT ?
MVC (Model-View-Controller) et MVT (Model-View-Template) sont quasi identiques. En Django, le « Controller » est géré par le framework lui-même (URL dispatcher). La View Django = le Controller MVC (logique). Le Template Django = la View MVC (présentation).

CHAPITRE 02

Projet, apps et structure

📦 Créer une app
# Un projet Django = plusieurs apps modulaires
python manage.py startapp blog

# Structure de l'app :
# blog/
# ├── __init__.py
# ├── admin.py (config admin)
# ├── apps.py (config app)
# ├── models.py (modèles / DB)
# ├── views.py (logique)
# ├── urls.py (routes — à créer)
# ├── tests.py (tests)
# └── migrations/ (migrations DB)

# monsite/settings.py — enregistrer l'app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Vos apps
'blog',
]

Chaque app = une fonctionnalité réutilisable. Un projet e-commerce pourrait avoir : products, users, orders, payments. Chaque app est autonome avec ses modèles, vues, URLs et templates.

CHAPITRE 03

Modèles et ORM

🗄️ Définir des modèles
# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(unique=True)

class Meta:
verbose_name_plural = « categories »

def __str__(self):
return self.name

class Post(models.Model):
# Champs
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
excerpt = models.TextField(blank=True)
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

# Relations
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)

class Meta:
ordering = ['-created_at']

def __str__(self):
return self.title

# Créer et appliquer les migrations
python manage.py makemigrations # Génère le fichier de migration
python manage.py migrate # Applique à la DB
📋 Types de champs courants
ChampTypeOptions courantes
CharFieldTexte courtmax_length (obligatoire)
TextFieldTexte longblank=True
IntegerFieldEntierdefault=0
DecimalFieldDécimal précismax_digits, decimal_places
BooleanFieldVrai/Fauxdefault=False
DateTimeFieldDate + heureauto_now_add, auto_now
SlugFieldURL-friendlyunique=True
EmailFieldEmail validéunique=True
ForeignKeyRelation N→1on_delete=CASCADE/SET_NULL
ManyToManyFieldRelation N→Nblank=True

CHAPITRE 04

Vues et URLs

🎯 Function-Based Views (FBV)
# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_list(request):
posts = Post.objects.filter(published=True)
return render(request, 'blog/post_list.html', )

def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug, published=True)
return render(request, 'blog/post_detail.html', )

🔗 Configuration des URLs
# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
path( », views.post_list, name='post_list'),
path('/', views.post_detail, name='post_detail'),
]

# monsite/urls.py — inclure les URLs de l'app
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]

🏭 Class-Based Views (CBV)
# blog/views.py — version CBV
from django.views.generic import ListView, DetailView, CreateView

class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
queryset = Post.objects.filter(published=True)
paginate_by = 10

class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
slug_field = 'slug'

# Dans urls.py avec CBV :
path( », PostListView.as_view(), name='post_list'),

FBV vs CBV : les Function-Based Views sont plus simples et explicites. Les Class-Based Views réduisent le code pour les opérations CRUD standard. Recommandation : FBV pour la logique custom, CBV (ListView, DetailView, CreateView, UpdateView, DeleteView) pour le CRUD standard.

CHAPITRE 05

Templates

📝 Syntaxe des templates Django

Blog

Articles

Aucun article.

Bienvenue

«  »>Connexion

📐 Template de base (héritage)




</span>Mon Site<span class="dc">

« >






Filtres utiles : , , , , , , .

CHAPITRE 06

Requêtes ORM avancées

🔍 QuerySet API
# Récupérer
Post.objects.all() # Tous
Post.objects.get(id=1) # Un seul (lève DoesNotExist si absent)
Post.objects.filter(published=True) # Filtrer
Post.objects.exclude(published=False) # Exclure
Post.objects.first() # Premier
Post.objects.last() # Dernier
Post.objects.count() # Nombre
Post.objects.exists() # Existe ?

# Lookups (double underscore __)
Post.objects.filter(title__contains='Django') # LIKE %Django%
Post.objects.filter(title__icontains='django') # Case-insensitive
Post.objects.filter(created_at__year=2025) # Par année
Post.objects.filter(created_at__gte=date) # ≥ date
Post.objects.filter(category__name='Tech') # Traverser relation
Post.objects.filter(id__in=[1, 2, 3]) # IN

# Chaîner
Post.objects.filter(published=True).order_by('-created_at')[:5]

# Q objects (OR, NOT)
from django.db.models import Q
Post.objects.filter(Q(title__contains='Django') | Q(title__contains='Python'))

# Agrégations
from django.db.models import Count, Avg
Category.objects.annotate(num_posts=Count('post'))

CHAPITRE 07

Formulaires

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

class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'category'] widgets =

# blog/views.py
from django.shortcuts import render, redirect
from .forms import PostForm

def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('blog:post_detail', slug=post.slug)
else:
form = PostForm()
return render(request, 'blog/post_form.html', )



est obligatoire dans tous les formulaires POST. Django protège automatiquement contre les attaques CSRF (Cross-Site Request Forgery). Sans ce token, le formulaire sera rejeté avec une erreur 403.

CHAPITRE 08

Authentification

🔐 Système d'auth intégré
# monsite/urls.py — ajouter les URLs d'auth
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'),
]

# settings.py
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

# Protéger une vue (FBV)
from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
return render(request, 'dashboard.html')

# Protéger une vue (CBV)
from django.contrib.auth.mixins import LoginRequiredMixin

class DashboardView(LoginRequiredMixin, TemplateView):
template_name = 'dashboard.html'

# Vue d'inscription custom
from django.contrib.auth.forms import UserCreationForm

def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('/')
else:
form = UserCreationForm()
return render(request, 'registration/register.html', )

CHAPITRE 09

Django REST Framework

🔌 API REST avec DRF
# pip install djangorestframework
# Ajouter 'rest_framework' dans INSTALLED_APPS

# blog/serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
author_name = serializers.CharField(source='author.username', read_only=True)

class Meta:
model = Post
fields = ['id', 'title', 'slug', 'content', 'author_name', 'created_at']

# blog/views.py — ViewSet (CRUD automatique)
from rest_framework import viewsets, permissions
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.filter(published=True)
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]

def perform_create(self, serializer):
serializer.save(author=self.request.user)

# blog/urls.py — Router DRF
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('posts', PostViewSet)

urlpatterns = [
path('api/', include(router.urls)),
]

# Endpoints générés automatiquement :
# GET /api/posts/ → Liste
# POST /api/posts/ → Créer
# GET /api/posts/1/ → Détail
# PUT /api/posts/1/ → Modifier
# DELETE /api/posts/1/ → Supprimer

DRF génère un CRUD complet en ~15 lignes (serializer + viewset + router). Il fournit aussi une interface web navigable pour tester l'API, la pagination, le filtrage, et l'authentification par tokens.

CHAPITRE 10

Admin

🛠️ Interface d'administration
# Créer un superuser
python manage.py createsuperuser
# → http://127.0.0.1:8000/admin/

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

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'category', 'published', 'created_at'] list_filter = ['published', 'category', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields =
list_editable = ['published'] date_hierarchy = 'created_at'
ordering = ['-created_at']

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug'] prepopulated_fields =

L'admin Django est un outil unique. En quelques lignes, vous obtenez une interface complète pour gérer vos données : liste filtrable, recherche, édition, actions en masse. Aucun autre framework ne propose un équivalent aussi complet.

CHAPITRE 11

Déploiement

🚀 Préparer la production
# settings.py — production
DEBUG = False
ALLOWED_HOSTS = ['monsite.com', 'www.monsite.com'] SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

# Base de données production
# pip install dj-database-url psycopg2-binary
import dj_database_url
DATABASES =

# Fichiers statiques
# pip install whitenoise
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # Après security

] STATIC_ROOT = BASE_DIR / 'staticfiles'

# Collecter les fichiers statiques
python manage.py collectstatic

# requirements.txt
pip freeze > requirements.txt

# Gunicorn — serveur WSGI production
pip install gunicorn
gunicorn monsite.wsgi:application –bind 0.0.0.0:8000

PlateformeIdéal pour
RailwayDéploiement simple (git push), PostgreSQL intégré
RenderFree tier, PostgreSQL, auto-deploy
Fly.ioConteneurs Docker, edge deployment
AWS / DigitalOceanVPS, contrôle total, Nginx + Gunicorn
PythonAnywhereHébergement Python spécialisé, simple

CHAPITRE 12

Bonnes pratiques

🏗️ Structure de projet
monsite/
├── manage.py
├── monsite/
│ ├── settings/
│ │ ├── base.py # Paramètres communs
│ │ ├── dev.py # DEBUG=True, sqlite
│ │ └── prod.py # DEBUG=False, PostgreSQL
│ ├── urls.py
│ └── wsgi.py
├── blog/
│ ├── models.py
│ ├── views.py
│ ├── urls.py
│ ├── forms.py
│ ├── serializers.py # Si DRF
│ ├── admin.py
│ ├── tests/
│ │ ├── test_models.py
│ │ └── test_views.py
│ └── templates/
│ └── blog/
│ ├── post_list.html
│ └── post_detail.html
├── templates/
│ └── base.html
├── static/
├── requirements.txt
└── .env
✅ Bonnes pratiques

✅ À FAIRE
• Apps modulaires et réutilisables
ModelForm pour les formulaires liés aux modèles
select_related / prefetch_related (N+1)
dans tous les forms
• Variables d'env (django-environ)
get_object_or_404 plutôt que try/except
• Admin personnalisé (list_display, search)
• Tests pour chaque vue et modèle
whitenoise pour les fichiers statiques

❌ À ÉVITER
DEBUG=True en production
• SECRET_KEY en dur dans le code
• Logique métier dans les vues (utiliser services)
• Requêtes N+1 (boucle + accès relation)
• Ignorer les migrations
objects.all() sans pagination
• Modèle User custom trop tard
ForeignKey sans on_delete
• Templates sans héritage (base.html)

🧠 Quiz
Qu'est-ce que le problème N+1 en Django ?
Quand vous itérez sur des objets et accédez à une relation dans la boucle, Django fait 1 requête pour la liste + N requêtes pour chaque relation. Solution : select_related('author') pour les ForeignKey (JOIN SQL) et prefetch_related('tags') pour les ManyToMany (2 requêtes au lieu de N+1).
Django vs Flask — quand choisir quoi ?
Django = projets structurés avec ORM, admin, auth, formulaires (e-commerce, CMS, SaaS, API). Flask = projets légers, microservices, API simples, prototypage rapide. Django = « batteries included », Flask = « assemble toi-même ».

Cours Django Complet — MVT, ORM, REST API et déploiement

Référence : docs.djangoproject.com | DRF