Exercices Django Avancé Mini API Blog complete
🎉

Bravo!

Avancé 🧠 Fondamentaux 30 XP 0 personnes ont réussi

Mini API Blog complete

C'est le projet final. Tu vas assembler tous les concepts vus dans cette serie pour construire une mini API de blog complete : serialisation, CRUD, permissions, pagination et validation. C'est exactement le genre de code que tu ecrirais dans un vrai projet Django avec DRF pour un client ou un side project, sauf qu'ici tout est en Python pur.

Tu vas créer une classe BlogAPI qui :

__init__() : initialise un dictionnaire d'articles (self.articles, id -> article), un compteur d'ids (self.next_id = 1), et un dictionnaire d'utilisateurs (self.users) avec au moins deux utilisateurs.

handle_request(request) : le point d'entrée de l'API. request est un dictionnaire avec les clés method, path, user (nom de l'utilisateur ou None), et data (optionnel). La méthode dispatche selon le chemin et la méthode HTTP :

GET /posts/ -> liste paginee (page dans request.get('params', {}))
POST /posts/ -> crée un article (authentification requise)
GET /posts/<id>/ -> detail d'un article
PUT /posts/<id>/ -> modifie un article (seul l'auteur peut modifier)
DELETE /posts/<id>/ -> supprime un article (seul l'auteur peut supprimer)

Regles :
- Un article a les champs : id, title, content, author, created_at (utilise 'now' comme valeur)
- La création requiert que le user soit non None (authentifie)
- La modification/suppression requiert que request['user'] == article['author']
- La liste est paginee (3 articles par page, utilise request.get('params', {}).get('page', 1))
- La création valide que title et content sont presents et non vides
- Les réponses ont un champ 'status' (200, 201, 204, 400, 403, 404)

Exemple :

api = BlogAPI()
api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'title': 'Hello', 'content': 'World'}})
renvoie {'status': 201, 'data': {'id': 1, 'title': 'Hello', 'content': 'World', 'author': 'alice', 'created_at': 'now'}}

Tests (6/7)

Création article
api = BlogAPI()
r = api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'title': 'Hello', 'content': 'World'}})
assert r['status'] == 201 and r['data']['author'] == 'alice' and r['data']['id'] == 1
Création sans auth
api = BlogAPI()
r = api.handle_request({'method': 'POST', 'path': '/posts/', 'user': None, 'data': {'title': 'Test', 'content': 'Body'}})
assert r['status'] == 403
Validation titre manquant
api = BlogAPI()
r = api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'content': 'Body'}})
assert r['status'] == 400 and 'title' in r.get('errors', {})
Modifier par auteur
api = BlogAPI()
api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'title': 'Old', 'content': 'Body'}})
r = api.handle_request({'method': 'PUT', 'path': '/posts/1/', 'user': 'alice', 'data': {'title': 'New'}})
assert r['status'] == 200 and r['data']['title'] == 'New'
Modifier par non-auteur refuse
api = BlogAPI()
api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'title': 'Test', 'content': 'Body'}})
r = api.handle_request({'method': 'PUT', 'path': '/posts/1/', 'user': 'bob', 'data': {'title': 'Hack'}})
assert r['status'] == 403
Pagination
api = BlogAPI()
for i in range(5):
    api.handle_request({'method': 'POST', 'path': '/posts/', 'user': 'alice', 'data': {'title': f'Post {i}', 'content': 'Body'}})
r = api.handle_request({'method': 'GET', 'path': '/posts/', 'user': None, 'params': {'page': 1}})
assert r['data']['count'] == 5 and len(r['data']['results']) == 3 and r['data']['next'] == 2

+ 0 tests cachés

Indices (3 disponibles)

solution.py