Django est le framework web Python de référence pour développer des applications web robustes et scalables. Utilisé par des géants comme Instagram, Pinterest et Mozilla, Django offre une architecture solide basée sur le pattern Model-View-Template (MVT) qui accélère considérablement le développement.
Pourquoi Choisir Django ?
Django se distingue par plusieurs caractéristiques qui en font un choix de premier plan :
- "Batteries incluses" : ORM, authentification, admin, cache... tout est intégré
- Sécurité par défaut : Protection contre CSRF, XSS, injection SQL
- Scalabilité : Conçu pour supporter une forte charge
- Communauté active : Documentation excellente et écosystème riche
- Productivité : Développement rapide avec moins de code
Installation et Configuration
Commençons par installer Django et créer notre premier projet :
# Installation de Django
pip install django
# Création d'un nouveau projet
django-admin startproject monsite
cd monsite
# Création d'une application
python manage.py startapp blog
# Lancement du serveur de développement
python manage.py runserver
Structure d'un Projet Django
monsite/
manage.py
monsite/
__init__.py
settings.py # Configuration
urls.py # Routage principal
wsgi.py # Interface serveur
blog/
__init__.py
admin.py # Interface d'administration
apps.py # Configuration de l'app
models.py # Modèles de données
views.py # Logique métier
urls.py # Routage de l'app
templates/ # Templates HTML
static/ # Fichiers statiques
Les Modèles : Votre Base de Données
Les modèles Django définissent la structure de vos données et génèrent automatiquement la base de données :
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class Categorie(models.Model):
nom = models.CharField(max_length=100)
description = models.TextField(blank=True)
def __str__(self):
return self.nom
class Meta:
verbose_name_plural = "Catégories"
class Article(models.Model):
STATUT_CHOICES = [
('brouillon', 'Brouillon'),
('publie', 'Publié'),
]
titre = models.CharField(max_length=200)
contenu = models.TextField()
auteur = models.ForeignKey(User, on_delete=models.CASCADE)
categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL, null=True)
date_creation = models.DateTimeField(auto_now_add=True)
date_modification = models.DateTimeField(auto_now=True)
statut = models.CharField(max_length=20, choices=STATUT_CHOICES, default='brouillon')
vues = models.PositiveIntegerField(default=0)
def __str__(self):
return self.titre
def get_absolute_url(self):
return reverse('article_detail', kwargs={'pk': self.pk})
class Meta:
ordering = ['-date_creation']
verbose_name_plural = "Articles"
class Commentaire(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='commentaires')
nom = models.CharField(max_length=100)
email = models.EmailField()
contenu = models.TextField()
date_creation = models.DateTimeField(auto_now_add=True)
actif = models.BooleanField(default=True)
def __str__(self):
return f'Commentaire de {self.nom} sur {self.article.titre}'
class Meta:
ordering = ['date_creation']
Migrations et Base de Données
# Création des migrations
python manage.py makemigrations
# Application des migrations
python manage.py migrate
# Création d'un superutilisateur
python manage.py createsuperuser
Les Vues : Logique de l'Application
Les vues Django gèrent la logique métier et retournent les réponses HTTP :
# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.core.paginator import Paginator
from django.contrib import messages
from django.db.models import Q
from .models import Article, Categorie, Commentaire
from .forms import CommentaireForm
def liste_articles(request):
"""Affiche la liste des articles publiés avec pagination"""
articles = Article.objects.filter(statut='publie')
# Recherche
recherche = request.GET.get('q')
if recherche:
articles = articles.filter(
Q(titre__icontains=recherche) |
Q(contenu__icontains=recherche)
)
# Filtrage par catégorie
categorie_id = request.GET.get('categorie')
if categorie_id:
articles = articles.filter(categorie_id=categorie_id)
# Pagination
paginator = Paginator(articles, 5)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj': page_obj,
'categories': Categorie.objects.all(),
'recherche': recherche,
}
return render(request, 'blog/liste_articles.html', context)
def detail_article(request, pk):
"""Affiche un article spécifique avec ses commentaires"""
article = get_object_or_404(Article, pk=pk, statut='publie')
# Incrémenter le compteur de vues
article.vues += 1
article.save(update_fields=['vues'])
# Gestion des commentaires
commentaires = article.commentaires.filter(actif=True)
nouveau_commentaire = None
if request.method == 'POST':
form = CommentaireForm(request.POST)
if form.is_valid():
nouveau_commentaire = form.save(commit=False)
nouveau_commentaire.article = article
nouveau_commentaire.save()
messages.success(request, 'Votre commentaire a été ajouté avec succès!')
return redirect('detail_article', pk=article.pk)
else:
form = CommentaireForm()
context = {
'article': article,
'commentaires': commentaires,
'form': form,
'nouveau_commentaire': nouveau_commentaire,
}
return render(request, 'blog/detail_article.html', context)
def articles_par_categorie(request, categorie_id):
"""Affiche les articles d'une catégorie spécifique"""
categorie = get_object_or_404(Categorie, pk=categorie_id)
articles = Article.objects.filter(categorie=categorie, statut='publie')
context = {
'categorie': categorie,
'articles': articles,
}
return render(request, 'blog/articles_categorie.html', context)
Les Templates : Interface Utilisateur
Django utilise un système de templates puissant pour générer le HTML :
Template de Base
<!-- blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Mon Blog Django{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'liste_articles' %}">Mon Blog</a>
<div class="navbar-nav">
<a class="nav-link" href="{% url 'liste_articles' %}">Articles</a>
{% for categorie in categories %}
<a class="nav-link" href="{% url 'articles_categorie' categorie.pk %}">
{{ categorie.nom }}
</a>
{% endfor %}
</div>
</div>
</nav>
<main class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
</main>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Template de Liste d'Articles
<!-- blog/templates/blog/liste_articles.html -->
{% extends 'blog/base.html' %}
{% block title %}Liste des Articles - {{ block.super }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-8">
<h1>Articles du Blog</h1>
{% if recherche %}
<p>Résultats pour : <strong>"{{ recherche }}"</strong></p>
{% endif %}
{% for article in page_obj %}
<article class="card mb-4">
<div class="card-body">
<h2 class="card-title">
<a href="{{ article.get_absolute_url }}" class="text-decoration-none">
{{ article.titre }}
</a>
</h2>
<p class="card-text">{{ article.contenu|truncatewords:30 }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">
Par {{ article.auteur.username }} le {{ article.date_creation|date:"d M Y" }}
{% if article.categorie %}
dans <a href="{% url 'articles_categorie' article.categorie.pk %}">
{{ article.categorie.nom }}
</a>
{% endif %}
</small>
<span class="badge bg-secondary">{{ article.vues }} vues</span>
</div>
</div>
</article>
{% empty %}
<p>Aucun article trouvé.</p>
{% endfor %}
{% include 'blog/pagination.html' %}
</div>
<div class="col-md-4">
{% include 'blog/sidebar.html' %}
</div>
</div>
{% endblock %}
Les Formulaires : Interaction Utilisateur
Django propose un système de formulaires robuste pour gérer les entrées utilisateur :
# blog/forms.py
from django import forms
from .models import Commentaire
class CommentaireForm(forms.ModelForm):
class Meta:
model = Commentaire
fields = ['nom', 'email', 'contenu']
widgets = {
'nom': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Votre nom'
}),
'email': forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': '[email protected]'
}),
'contenu': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Votre commentaire...'
}),
}
def clean_contenu(self):
contenu = self.cleaned_data['contenu']
if len(contenu) < 10:
raise forms.ValidationError("Le commentaire doit contenir au moins 10 caractères.")
return contenu
class RechercheForm(forms.Form):
recherche = forms.CharField(
max_length=200,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Rechercher...'
})
)
Configuration des URLs
Le système de routage Django permet de définir des URLs élégantes :
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.liste_articles, name='liste_articles'),
path('article/<int:pk>/', views.detail_article, name='detail_article'),
path('categorie/<int:categorie_id>/', views.articles_par_categorie, name='articles_categorie'),
]
# monsite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
Interface d'Administration
Django propose une interface d'administration automatique très puissante :
# blog/admin.py
from django.contrib import admin
from .models import Article, Categorie, Commentaire
@admin.register(Categorie)
class CategorieAdmin(admin.ModelAdmin):
list_display = ['nom', 'description']
search_fields = ['nom']
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['titre', 'auteur', 'categorie', 'statut', 'date_creation', 'vues']
list_filter = ['statut', 'categorie', 'date_creation']
search_fields = ['titre', 'contenu']
prepopulated_fields = {'slug': ('titre',)}
date_hierarchy = 'date_creation'
fieldsets = (
('Contenu', {
'fields': ('titre', 'contenu', 'categorie')
}),
('Métadonnées', {
'fields': ('auteur', 'statut'),
'classes': ('collapse',)
}),
)
@admin.register(Commentaire)
class CommentaireAdmin(admin.ModelAdmin):
list_display = ['nom', 'email', 'article', 'date_creation', 'actif']
list_filter = ['actif', 'date_creation']
search_fields = ['nom', 'email', 'contenu']
actions = ['approuver_commentaires']
def approuver_commentaires(self, request, queryset):
queryset.update(actif=True)
approuver_commentaires.short_description = "Approuver les commentaires sélectionnés"
API REST avec Django REST Framework
Pour créer des APIs modernes, Django REST Framework est l'extension de référence :
# Installation
pip install djangorestframework
# serializers.py
from rest_framework import serializers
from .models import Article, Categorie
class CategorieSerializer(serializers.ModelSerializer):
class Meta:
model = Categorie
fields = '__all__'
class ArticleSerializer(serializers.ModelSerializer):
auteur = serializers.StringRelatedField(read_only=True)
categorie = CategorieSerializer(read_only=True)
class Meta:
model = Article
fields = ['id', 'titre', 'contenu', 'auteur', 'categorie', 'date_creation', 'vues']
# API views
from rest_framework import generics
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class ArticleListAPI(generics.ListCreateAPIView):
queryset = Article.objects.filter(statut='publie')
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
class ArticleDetailAPI(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.filter(statut='publie')
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
Déploiement et Production
Préparer votre application Django pour la production :
Configuration de Production
# settings/production.py
from .base import *
DEBUG = False
ALLOWED_HOSTS = ['votre-domaine.com', 'www.votre-domaine.com']
# Base de données PostgreSQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'votre_db',
'USER': 'votre_user',
'PASSWORD': 'votre_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
# Fichiers statiques
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Sécurité
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Collecte des Fichiers Statiques
# Collecte des fichiers statiques
python manage.py collectstatic
# Configuration Nginx
server {
listen 80;
server_name votre-domaine.com;
location /static/ {
alias /path/to/your/staticfiles/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Bonnes Pratiques Django
- Structure modulaire : Divisez votre projet en applications logiques
- Migrations : Toujours versionner vos changements de base de données
- Tests : Écrivez des tests pour vos modèles, vues et formulaires
- Sécurité : Utilisez HTTPS, validez les entrées, protégez contre les attaques
- Cache : Implémentez la mise en cache pour améliorer les performances
- Documentation : Documentez votre code et vos APIs
Conclusion
Django est un framework extrêmement puissant qui permet de développer rapidement des applications web complexes. Sa philosophie "batteries incluses" et son architecture MVT bien structurée en font un choix excellent pour les projets professionnels.
De la gestion des utilisateurs à l'interface d'administration, en passant par l'ORM et les APIs REST, Django couvre tous les aspects du développement web moderne. Sa courbe d'apprentissage peut paraître importante au début, mais l'investissement en vaut largement la peine.
Maîtrisez Django avec les Experts
Rejoignez notre formation "Développement Web avec Django" et apprenez à créer des applications web professionnelles avec l'accompagnement de nos formateurs expérimentés.
En Savoir Plus