Django REST + Wagtail Python 3.11+ PostgreSQL

Vue d'ensemble

Backend API Django REST pour la plateforme média panafricaine GAM

7
Apps Django
core, users, editorial, engagement, search, kpi, advertising
12
Modèles
User, Article, Video, Category, Author, Newsletter...
40+
Endpoints API
CRUD, actions, recherche, homepage
10
User Stories
US-01 à US-10 implémentées

User Stories Implémentées

USDescriptionStatut
US-01Gestion des taxonomies (Auteurs & Catégories)Fait
US-02Rédaction d'article riche (blocs dynamiques StreamField)Fait
US-03Gestion des vidéos Web TV (YouTube)Fait
US-04Workflow de publication (Draft → Published)Fait
US-05Page d'accueil dynamiqueFait
US-06Lecture d'article (vues, articles liés)Fait
US-07Consultation Web TVFait
US-08Recherche de contenuFait
US-10Inscription à la newsletterFait
US-11SEO (meta title, meta description)Fait

Architecture

Organisation modulaire avec séparation claire Core / Métier

Client
Frontend Next.js
Wagtail CMS Admin
Django Admin
API Layer
DRF ViewSets
JWT Auth
Swagger / ReDoc
Apps Métier
Editorial
Engagement
Search
KPI
Advertising
Apps Core
Core (models, mixins, utils)
Users (auth, rôles)
Infrastructure
PostgreSQL
Supabase Storage
Brevo / Mailchimp
Cloudflare Tunnel

Apps Core

  • core — Modèles de base (TimeStamped, Slugged, Publishable, SEO, Ordered), permissions, utilitaires, validators, throttles, storage Supabase
  • users — Modèle User custom (email-based), rôles (Admin, Chef Rédacteur, Rédacteur, Lecteur), JWT + Session auth

Apps Métier

  • editorial — Articles avec StreamField, Vidéos YouTube, Catégories hiérarchiques, Auteurs
  • engagement — Newsletter (Brevo/Mailchimp), Messages de contact, Notifications
  • search — Recherche globale, Suggestions, Tags populaires
  • kpi — KPI plateforme (articles, vidéos, lecteurs, pays couverts)
  • advertising — Publicités (placement, planification, tracking impressions/clics, CTR)

Modèles de données

Schéma complet des modèles Django avec leurs champs et relations

Modèles Abstraits (Core)

TimeStampedModel

abstract
created_atDateTimeFieldauto_now_add
updated_atDateTimeFieldauto_now

SluggedModel

abstract
slugSlugField(255)unique

PublishableModel

abstract
statusCharFielddraft|published
published_atDateTimeFieldnullable

SEOModel

abstract
meta_titleCharField(70)
meta_descriptionTextField(160)

OrderedModel

abstract
orderPositiveIntegerField

Users

User

AbstractUser + TimeStamped
emailEmailFieldunique, PK auth
first_nameCharField(150)
last_nameCharField(150)
avatarImageField
bioTextField(500)
roleCharFieldadmin|chief_editor|editor|viewer
is_adminis_chief_editoris_editorcan_publishcan_edit_content

Editorial

Article

TimeStamped + Slugged + Publishable + SEO
titleCharField(255)
excerptTextField(300)
featured_imageImageField
external_image_urlURLField
featured_image_captionCharField(255)
authorFK → AuthorPROTECT
categoryFK → CategoryPROTECT
tagsCharField(500)
bodyStreamFieldWagtail blocs
contentTextFieldlegacy HTML
reading_timePositiveIntegerField
views_countPositiveIntegerField
is_featuredBooleanField
is_trendingBooleanField
created_byFK → User
updated_byFK → User

ArticleBlock

TimeStamped
articleFK → ArticleCASCADE
block_typeCharFieldtext|image|quote|video|tweet|heading|list|code
orderPositiveIntegerField
contentTextField
imageImageField
embed_urlURLField
metadataJSONField

Video

TimeStamped + Slugged + Publishable + SEO
titleCharField(255)
descriptionTextField
youtube_urlURLFieldvalidated
youtube_idCharField(20)
thumbnailImageField
youtube_thumbnailURLField
video_typeCharFieldemission|reportage|interview|documentary|short
categoryFK → CategorySET_NULL
durationPositiveIntegerField
views_countPositiveIntegerField
is_featuredBooleanField
is_liveBooleanField

Category

TimeStamped + Slugged + Ordered
nameCharField(100)unique
descriptionTextField
colorCharField(7)#hex
iconCharField(50)
imageImageField
parentFK → selfhiérarchie
is_activeBooleanField
is_featuredBooleanField

Author

TimeStamped + Slugged
userO2O → Useroptional
nameCharField(255)
photoImageField
bioTextField
emailEmailField
twitterURLField
linkedinURLField
websiteURLField
is_activeBooleanField

Engagement

NewsletterSubscription

TimeStamped
emailEmailFieldunique
statusCharFieldpending|confirmed|unsubscribed
ip_addressGenericIPAddressField
sourceCharField(100)
external_idCharField(100)Brevo/Mailchimp

ContactMessage

TimeStamped
nameCharField(100)
emailEmailField
subjectCharField(200)
messageTextField
statusCharFieldnew|read|replied|archived

ArticleNotification

TimeStamped
article_idPositiveBigIntegerFieldunique
campaign_idCharField(100)
statusCharFieldpending|sent|failed

KPI & Advertising

PlatformKPI

singleton
total_articlesPositiveIntegerField
total_videosPositiveIntegerField
monthly_readersPositiveIntegerField
total_viewsPositiveIntegerField
countries_coveredPositiveIntegerField
tv_expertsPositiveIntegerField
total_authorsPositiveIntegerField

Advertisement

TimeStamped
titleCharField(255)
advertiser_nameCharField(255)
imageImageField
external_urlURLField(500)
ad_typeCharFieldLEADERBOARD|BANNER|SIDEBAR|NATIVE|IN_ARTICLE|INTERSTITIEL
positionCharField8 emplacements
start_date / end_dateDateField
statusCharFieldDRAFT|ACTIVE|PAUSED|EXPIRED
impressions_countPositiveIntegerField
clicks_countPositiveIntegerField
price_per_monthDecimalField
ctris_currently_active

Core — Réseaux Sociaux

SocialNetwork

TimeStamped
networkCharField(30)facebook|linkedin|whatsapp|tiktok|youtube|instagram|twitter|telegram|snapchat|other
labelCharField(100)
urlURLField
is_activeBooleanField
orderPositiveIntegerField

Endpoints API

Tous les endpoints REST sous /api/v1/

Auth — /api/v1/auth/

POST/api/v1/auth/login/Connexion — retourne JWT + infos user
POST/api/v1/auth/refresh/Rafraîchir le access token
POST/api/v1/auth/register/Inscription nouvel utilisateur
POST/api/v1/auth/logout/Déconnexion (blacklist token)
GET/api/v1/auth/profile/Profil utilisateur connecté
PUT/api/v1/auth/profile/Modifier son profil
POST/api/v1/auth/password/change/Changer son mot de passe
GET/api/v1/auth/admin/users/Liste utilisateurs (admin)
POST/api/v1/auth/admin/users/{id}/activate/Activer un utilisateur
POST/api/v1/auth/admin/users/{id}/deactivate/Désactiver un utilisateur
POST/api/v1/auth/admin/users/{id}/change_role/Changer le rôle

Editorial — /api/v1/editorial/

GET/api/v1/editorial/articles/Liste paginée des articles
GET/api/v1/editorial/articles/{slug}/Détail article (incrémente vues)
POST/api/v1/editorial/articles/Créer un article
GET/api/v1/editorial/articles/featured/Articles à la Une (US-05)
GET/api/v1/editorial/articles/trending/Articles tendance
GET/api/v1/editorial/articles/recent/Articles récents
POST/api/v1/editorial/articles/{slug}/publish/Publier (US-04, permission CanPublish)
POST/api/v1/editorial/articles/{slug}/unpublish/Dépublier
GET/api/v1/editorial/videos/Liste paginée des vidéos
GET/api/v1/editorial/videos/{slug}/Détail vidéo
GET/api/v1/editorial/videos/featured/Vidéos en vedette
GET/api/v1/editorial/videos/live/Vidéos en direct
GET/api/v1/editorial/videos/by_type/?type=Vidéos par type (US-07)
GET/api/v1/editorial/categories/Liste catégories (racines, cache 10min)
GET/api/v1/editorial/categories/{slug}/Détail catégorie + enfants
GET/api/v1/editorial/categories/{slug}/articles/Articles d'une catégorie
GET/api/v1/editorial/categories/{slug}/videos/Vidéos d'une catégorie
GET/api/v1/editorial/categories/featured/Catégories en vedette
GET/api/v1/editorial/authors/Liste auteurs actifs
GET/api/v1/editorial/authors/{slug}/Détail auteur
GET/api/v1/editorial/authors/{slug}/articles/Articles d'un auteur
GET/api/v1/editorial/homepage/Données page d'accueil (cache 5min)

Search — /api/v1/search/

GET/api/v1/search/?q=queryRecherche articles + vidéos
GET/api/v1/search/global/?q=queryRecherche globale paginée
GET/api/v1/search/suggestions/?q=querySuggestions auto-complete
GET/api/v1/search/trending-tags/Tags populaires

Engagement — /api/v1/engagement/

POST/api/v1/engagement/newsletter/subscribe/Inscription newsletter
POST/api/v1/engagement/newsletter/unsubscribe/Désabonnement newsletter
POST/api/v1/engagement/contact/Envoyer message de contact
GET/api/v1/engagement/admin/newsletter/Admin — Liste abonnés
GET/api/v1/engagement/admin/contacts/Admin — Messages de contact

KPI — /api/v1/kpi/

GET/api/v1/kpi/platform/KPIs plateforme (articles, vidéos, pays...)

Advertising — /api/v1/advertising/

GET/api/v1/advertising/active/Publicités actives (par position)
POST/api/v1/advertising/track/Tracker impression/clic
GET/api/v1/advertising/admin/Admin — CRUD publicités (staff only)

Core — /api/v1/core/

GET/api/v1/core/social-networks/Réseaux sociaux du site

Documentation API

GET/api/schema/Schéma OpenAPI (JSON)
GET/api/docs/Swagger UI interactif
GET/api/redoc/ReDoc documentation

Authentification & Rôles

JWT + Session hybride avec système de rôles hiérarchique

JWT (API)

POST /api/v1/auth/login/
{
  "email": "user@example.com",
  "password": "***"
}

→ Response:
{
  "access": "eyJ...",   // 60 min
  "refresh": "eyJ...",  // 7 jours
  "user": { ... }
}

// Utilisation
Authorization: Bearer <access_token>

Session (Wagtail CMS)

// Login JWT crée aussi une session Django
// → Accès automatique au CMS Wagtail

POST /api/v1/auth/session/login/
  → Redirige vers /cms/

GET /api/v1/auth/session/logout/
  → Détruit la session

Hiérarchie des rôles

Admin
Tout accès + gestion utilisateurs
↓ hérite
Rédacteur en chef
Publier / Dépublier le contenu
↓ hérite
Rédacteur
Créer / Modifier du contenu
Lecteur
Lecture seule (API publique)

Permissions custom

PermissionDescriptionRôles
IsEditorOrReadOnlyÉcriture réservée aux rédacteurs+Editor, Chief Editor, Admin
CanPublishPublier / dépublier du contenuChief Editor, Admin
IsAdminUserGestion des utilisateursAdmin uniquement

Rate Limiting

ScopeLimiteType
Anonyme2 000 / heureLecture (GET safe) — écriture limitée par IP
Authentifié5 000 / heureToutes requêtes
Newsletter10 / heureInscription newsletter
Contact5 / heureMessages de contact

Stack Technique

Technologies et dépendances du backend

🐍

Django 5.x

Framework web Python

🔌

Django REST Framework

API REST avec ViewSets, Serializers, Filters

📝

Wagtail CMS

CMS headless avec StreamField pour les articles

🔑

SimpleJWT

Authentification JWT + blacklist + session hybride

🐘

PostgreSQL

Base de données relationnelle

☁️

Supabase Storage

Stockage média S3-compatible

📧

Brevo / Mailchimp

Fournisseur newsletter

🌐

Cloudflare Tunnel

Exposition sécurisée via Workers

📖

drf-spectacular

Documentation OpenAPI / Swagger / ReDoc

🔍

django-filter

Filtres avancés sur les endpoints

🐳

Docker

Conteneurisation avec Dockerfile + entrypoint

WhiteNoise

Serveur de fichiers statiques

Configuration clé

Settings

LANGUAGE_CODE = 'fr-fr'
TIME_ZONE     = 'Africa/Douala'
PAGE_SIZE     = 12

# JWT
ACCESS_TOKEN_LIFETIME  = 60 min
REFRESH_TOKEN_LIFETIME = 7 jours
ROTATE_REFRESH_TOKENS  = True

# Cache
Homepage       = 5 min
Categories     = 10 min

# Lecture
READING_SPEED  = 200 mots/min

Middleware Stack

1. SecurityMiddleware
2. WhiteNoiseMiddleware
3. CorsMiddleware
4. SessionMiddleware
5. CommonMiddleware
6. CsrfViewMiddleware
7. AuthenticationMiddleware
8. MessageMiddleware
9. XFrameOptionsMiddleware
10. Wagtail RedirectMiddleware

Structure du Projet

Organisation des fichiers et dossiers

GAM-backend/
├── config/
│   ├── settings/
│   │   ├── base.py            # Settings communs (JWT, DRF, Wagtail, CORS...)
│   │   ├── development.py     # Debug, SQLite fallback
│   │   └── production.py      # Sécurité, Supabase, Gunicorn
│   ├── urls.py                # Routes principales + Swagger
│   ├── wsgi.py
│   └── asgi.py
├── apps/
│   ├── core/                  # Modèles abstraits, permissions, utilitaires
│   │   ├── models.py          # TimeStamped, Slugged, Publishable, SEO, Ordered, SocialNetwork
│   │   ├── permissions.py     # IsEditorOrReadOnly, CanPublish
│   │   ├── validators.py      # validate_youtube_url, validate_hex_color
│   │   ├── utils.py           # extract_youtube_id, get_youtube_embed_url
│   │   ├── throttles.py       # ReadSafeAnonRateThrottle
│   │   ├── storage.py         # GAMBaseStorage (Supabase S3)
│   │   ├── mixins.py          # MultiSerializerViewSetMixin
│   │   └── constants.py       # AFRICAN_COUNTRIES
│   ├── users/                 # Authentification & Utilisateurs
│   │   ├── models.py          # User (email-based, 4 rôles)
│   │   ├── views.py           # Login, Register, Logout, Profile, Admin
│   │   ├── serializers.py     # JWT, User CRUD, Admin
│   │   └── urls.py
│   ├── editorial/             # Contenu éditorial
│   │   ├── models/
│   │   │   ├── article.py     # Article + ArticleBlock (StreamField)
│   │   │   ├── video.py       # Video (YouTube)
│   │   │   ├── category.py    # Category (hiérarchique, couleur)
│   │   │   └── author.py      # Author (bio, réseaux sociaux)
│   │   ├── views.py           # 4 ViewSets + HomepageView
│   │   ├── serializers.py     # List/Detail/Create/Admin serializers
│   │   ├── filters.py         # ArticleFilter, VideoFilter
│   │   ├── blocks.py          # ArticleStreamBlock (Wagtail)
│   │   └── signals.py         # Auto-calcul reading_time
│   ├── engagement/            # Newsletter & Contact
│   │   ├── models.py          # Newsletter, ContactMessage, Notifications
│   │   ├── services.py        # Brevo/Mailchimp integration
│   │   ├── views.py
│   │   └── urls.py
│   ├── search/                # Recherche
│   │   ├── services.py        # Logique de recherche
│   │   ├── views.py           # Search, Suggestions, Trending
│   │   └── urls.py
│   ├── kpi/                   # Indicateurs de performance
│   │   ├── models.py          # PlatformKPI (singleton)
│   │   └── views.py
│   └── advertising/           # Publicités
│       ├── models.py          # Advertisement (6 types, 8 positions)
│       ├── views.py           # Active ads, Track, Admin CRUD
│       └── urls.py
├── requirements/
│   ├── base.txt
│   ├── development.txt
│   └── production.txt
├── scripts/                   # Scripts utilitaires
├── Dockerfile
├── docker-entrypoint.sh
└── manage.py