Retour au Blog

Développement Web avec Django

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 :

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

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