Prérequis et installation
Assurez-vous que Python et pip sont installés sur votre système. Installez ensuite Django et les packages supplémentaires nécessaires.
pip install django celery redis django-bootstrap4
Structure de l'application
Créez le projet Django et une application dédiée aux alertes.
django-admin startproject mon_projet
cd mon_projet
python manage.py startapp alertes
Modèle de données pour les alertes
Définisons un modèle pour stocker les notifications destinées aux utilisateurs.
# alertes/models.py
from django.db import models
from django.contrib.auth import get_user_model
Utilisateur = get_user_model()
class Alerte(models.Model):
destinataire = models.ForeignKey(Utilisateur, on_delete=models.CASCADE, related_name='alertes_recues')
contenu = models.TextField()
date_creation = models.DateTimeField(auto_now_add=True)
est_consultee = models.BooleanField(default=False)
type_alerte = models.CharField(max_length=50, choices=[('info', 'Information'), ('action', 'Action requise'), ('erreur', 'Erreur')], default='info')
class Meta:
ordering = ['-date_creation']
verbose_name = 'Alerte utilisateur'
verbose_name_plural = 'Alertes utilisateurs'
def __str__(self):
return f"Alerte {self.pk} pour {self.destinataire.username}"
Configuration des signaux Django
Nous utiliserons les signaux Django pour déclencher la création d'alertes lors d'événements spécifiques.
# alertes/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Alerte
@receiver(post_save, sender='auth.User')
def creer_alerte_bienvenue(sender, instance, created, **kwargs):
"""Crée une alerte de bienvenue pour chaque nouvel utilisateur inscrit."""
if created:
Alerte.objects.create(
destinataire=instance,
contenu=f"Bienvenue {instance.username} ! Votre compte a été créé avec succès.",
type_alerte='info'
)
Vues pour la gestion des alertes
Implémentez les vues pour afficher, marquer comme lues et supprimer les alertes.
# alertes/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from .models import Alerte
@login_required
def liste_alertes(request):
"""Affiche la liste des alertes de l'utilisateur connecté."""
alertes_utilisateur = Alerte.objects.filter(destinataire=request.user)
nb_non_lues = alertes_utilisateur.filter(est_consultee=False).count()
contexte = {
'alertes': alertes_utilisateur,
'compteur_non_lues': nb_non_lues,
'est_page_liste': True
}
return render(request, 'alertes/liste.html', contexte)
@login_required
def marquer_comme_lue(request, alerte_id):
"""Marque une alerte spécifique comme lue."""
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
alerte = get_object_or_404(Alerte, pk=alerte_id, destinataire=request.user)
alerte.est_consultee = True
alerte.save()
nouveau_compteur = Alerte.objects.filter(destinataire=request.user, est_consultee=False).count()
return JsonResponse({'succes': True, 'nouveau_compteur': nouveau_compteur})
return JsonResponse({'succes': False}, status=400)
@login_required
def supprimer_alerte(request, alerte_id):
"""Supprime une alerte."""
alerte = get_object_or_404(Alerte, pk=alerte_id, destinataire=request.user)
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
alerte.delete()
nouveau_compteur = Alerte.objects.filter(destinataire=request.user, est_consultee=False).count()
return JsonResponse({'succes': True, 'nouveau_compteur': nouveau_compteur})
alerte.delete()
return redirect('liste_alertes')
@login_required
def compteur_alertes(request):
"""Retourne le nombre d'alertes non lues (pour appel AJAX)."""
nombre = Alerte.objects.filter(destinataire=request.user, est_consultee=False).count()
return JsonResponse({'compteur': nombre})
Configuration des URLs
Définissez les routes correspondant aux vues des alertes.
# alertes/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.liste_alertes, name='liste_alertes'),
path('marquer-lue/<int:alerte_id>/', views.marquer_comme_lue, name='marquer_comme_lue'),
path('supprimer/<int:alerte_id>/', views.supprimer_alerte, name='supprimer_alerte'),
path('compteur/', views.compteur_alertes, name='compteur_alertes'),
]
N'incluez ces URLs dans le fichier principal du projet.
# mon_projet/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('mes-alertes/', include('alertes.urls')),
# ... autres URLs ...
]
Gabarits HTML avec Bootstrap
Créez le modèle d'affichage principal pour les alertes, intégrant Bootstrap pour la présentation.
<!-- alertes/templates/alertes/liste.html -->
{% load static %}
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mes Alertes</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Centre de Notifications</h1>
<span class="badge bg-primary rounded-pill" id="badge-compteur">
{{ compteur_non_lues }}
</span>
</div>
<div class="row">
<div class="col-md-3">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action active">Toutes les alertes</a>
<a href="#" class="list-group-item list-group-item-action">Non lues ({{ compteur_non_lues }})</a>
</div>
</div>
<div class="col-md-9">
<div class="list-group">
{% for alerte in alertes %}
<div class="list-group-item list-group-item-action d-flex justify-content-between align-items-start"
id="alerte-{{ alerte.pk }}"
data-id="{{ alerte.pk }}">
<div class="ms-2 me-auto">
<div class="fw-bold {{ alerte.est_consultee|yesno:'text-muted,' }}">
{{ alerte.get_type_alerte_display }}
</div>
{{ alerte.contenu }}
<small class="text-muted d-block mt-1">{{ alerte.date_creation|timesince }}</small>
</div>
<div class="btn-group btn-group-sm">
{% if not alerte.est_consultee %}
<button type="button" class="btn btn-outline-primary btn-marquer-lue"
data-url="{% url 'marquer_comme_lue' alerte.pk %}">
Lue
</button>
{% endif %}
<button type="button" class="btn btn-outline-danger btn-supprimer"
data-url="{% url 'supprimer_alerte' alerte.pk %}">
×
</button>
</div>
</div>
{% empty %}
<div class="list-group-item text-center py-4">
<p class="text-muted mb-0">Aucune alerte pour le moment.</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script>
$(document).ready(function() {
function rafraichirCompteur() {
$.get("{% url 'compteur_alertes' %}", function(data) {
$('#badge-compteur').text(data.compteur);
});
}
setInterval(rafraichirCompteur, 30000);
$('.btn-marquer-lue').click(function() {
var btn = $(this);
var url = btn.data('url');
$.post(url, {
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(data) {
if (data.succes) {
btn.closest('.list-group-item').find('.fw-bold').addClass('text-muted');
btn.fadeOut(300, function() { $(this).remove(); });
$('#badge-compteur').text(data.nouveau_compteur);
}
});
});
$('.btn-supprimer').click(function() {
if (confirm("Supprimer cette alerte définitivement ?")) {
var btn = $(this);
var url = btn.data('url');
var element = btn.closest('.list-group-item');
$.post(url, {
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(data) {
if (data.succes) {
element.slideUp(300, function() { $(this).remove(); });
$('#badge-compteur').text(data.nouveau_compteur);
}
});
}
});
});
</script>
</body>
</html>
Traitement asynchrone avec Celery
Pour les opérations coûteuses, configurez Celery avec Redis comme broker.
# mon_projet/celery.py
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mon_projet.settings')
application = Celery('mon_projet')
application.config_from_object('django.conf:settings', namespace='CELERY')
application.autodiscover_tasks()
Ajoutez la configuration dans settings.py :
# Configuration Celery dans settings.py
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
Définissez une tâche asynchrone pour générer des alertes en arrière-plan.
# alertes/tasks.py
from celery import shared_task
from .models import Alerte
from django.contrib.auth import get_user_model
Utilisateur = get_user_model()
@shared_task
def envoyer_alerte_masse(type_alerte, contenu, liste_utilisateurs_ids):
"""Envoie une alerte à une liste d'utilisateurs."""
utilisateurs = Utilisateur.objects.filter(pk__in=liste_utilisateurs_ids)
alertes_a_creer = []
for user in utilisateurs:
alertes_a_creer.append(
Alerte(destinataire=user, contenu=contenu, type_alerte=type_alerte)
)
Alerte.objects.bulk_create(alertes_a_creer)
return f"{len(alertes_a_creer)} alertes envoyées."
@shared_task
def generer_rapport_alertes(user_id):
"""Génère un rapport résumé des alertes pour un utilisateur."""
utilisateur = Utilisateur.objects.get(pk=user_id)
total = Alerte.objects.filter(destinataire=utilisateur).count()
non_lues = Alerte.objects.filter(destinataire=utilisateur, est_consultee=False).count()
# ... logique de génération de rapport ...
return f"Rapport généré pour {utilisateur.username}. {non_lues} alertes non lues."
Déclenchez ces tâches depuis vos vues ou autres services.
# Exemple d'utilisation dans une vue
from .tasks import envoyer_alerte_masse, generer_rapport_alertes
def vue_admin_alertes(request):
if request.method == 'POST' and request.user.is_staff:
type_alerte = request.POST.get('type')
contenu = request.POST.get('contenu')
tous_utilisateurs = list(Utilisateur.objects.values_list('id', flat=True))
# Exécution asynchrone
envoyer_alerte_masse.delay(type_alerte, contenu, tous_utilisateurs)
return redirect('tableau_bord_admin')
# ... reste de la vue ...
Configuration du projet principal
Assurez-vous que l'application alertes est enregistrée dans INSTALLED_APPS et que les signaux sont chargés.
# alertes/apps.py
from django.apps import AppConfig
class AlertesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'alertes'
verbose_name = "Système d'alertes"
def ready(self):
import alertes.signals # Charge les signaux
# Dans settings.py
INSTALLED_APPS = [
...
'alertes.apps.AlertesConfig',
'bootstrap4',
...
]