todos à l’aide d’une migration, puis traiter des requêtes de création, de lecture, de mise à jour et de suppression à partir d’une petite vue JSON. Toutes les connexions utilisent TLS avec une vérification complète des certificats.
Prérequis
- Python 3.12 ou version ultérieure, requis par Django 6. Ce guide a été testé avec Python 3.13, Django 6.1.1 et psycopg 3.3.6.
- Un compte ClickHouse Cloud
psql, pour créer la base de données. Vous pouvez également exécuter le statementCREATE DATABASEdans la console SQL.
Créer un service ClickHouse Managed Postgres
Dans la console ClickHouse Cloud, cliquez sur New service et sélectionnez Postgres. L’instance est prête en quelques minutes. Pour une présentation pas à pas, consultez le guide de démarrage rapide.Obtenir vos informations de connexion
Cliquez sur Connect dans la barre latérale gauche de votre service. La fenêtre modale affiche votre nom d’utilisateur, votre mot de passe, le serveur et le port, ainsi qu’un sélecteur Directly / via PgBouncer. Dans ce guide, Django se connecte via le PgBouncer intégré, sur le port6432. En production, une application Django exécute généralement plusieurs processus worker répartis sur plusieurs machines, et chacun ouvre ses propres connexions. PgBouncer fonctionne en mode pool de connexions par transaction, ce qui permet à tous ces processus de partager un nombre réduit de connexions Postgres. Le pool de connexions par transaction impose de configurer quelques paramètres dans Django ; ce guide les inclut et les explique dans la section Paramètres PgBouncer pour Django.
Sélectionnez via PgBouncer pour afficher les informations de connexion au pool. Le port passe à 6432 et la chaîne de connexion inclut sslmode=verify-full.
Dans la fenêtre modale, cliquez sur Download CA certificate. Vous pouvez également le télécharger depuis Settings → CA Certificate. Ce certificat étant propre à votre instance, le driver peut s’en servir pour vérifier qu’il communique bien avec votre serveur.
Configurer le projet
Créez un répertoire de projet doté d’un environnement virtuel, installez Django et psycopg 3, puis créez un projet Django contenant une applicationtodos :
psycopg[binary] inclut une version récente de libpq : vous n’avez donc pas besoin d’installer PostgreSQL en local.
Configurer la connexion
Déplacez le certificat de CA que vous avez téléchargé dans le répertoire du projet, à côté demanage.py, puis renommez-le en ca-certificate.pem.
Créez une base de données pour l’application. Remplacez <PASSWORD> et l’hôte par les valeurs indiquées dans la fenêtre modale Connect :
mysite/settings.py, ajoutez import os sous from pathlib import Path, puis remplacez le paramètre DATABASES par :
mysite/settings.py
OPTIONS à psycopg, qui les relaie à son tour à libpq. Lorsque sslmode est défini sur verify-full, libpq vérifie que le certificat du serveur est signé par l’autorité de certification (CA) de votre service et qu’il correspond au nom d’hôte. BASE_DIR rend le chemin du certificat absolu : le paramètre fonctionne donc quel que soit le répertoire depuis lequel vous démarrez Django.
Si
sslrootcert pointe vers la mauvaise CA, la connexion échoue avec l’erreur SSL error: certificate verify failed. Sans sslrootcert, elle échoue avec root certificate file ".../.postgresql/root.crt" does not exist, car libpq se rabat alors sur l’emplacement par défaut de sa CA. Si vous vous connectez par adresse IP plutôt que par nom d’hôte, elle échoue avec server certificate for "..." does not match host name.Créer un modèle et exécuter les migrations
Ajoutez l’applicationtodos à INSTALLED_APPS dans mysite/settings.py :
mysite/settings.py
Todo dans todos/models.py :
todos/models.py
todos_todo, migrate crée les tables des applications intégrées de Django, telles que auth_user et django_session, et enregistre les migrations appliquées dans django_migrations. Pour une application existante, ne créez pas le modèle todos et exécutez uniquement python manage.py migrate, qui crée toutes les tables de votre application dans la nouvelle base de données.
migrate ne prend pas de verrou consultatif au niveau de la session ; il fonctionne donc via PgBouncer. En revanche, il n’empêche pas les exécutions simultanées : lancez donc les migrations depuis un seul endroit, par exemple une étape de déploiement unique.
Ajouter un endpoint JSON
Remplaceztodos/views.py par deux vues qui permettent de créer, lister, mettre à jour et supprimer des tâches à l’aide de l’ORM Django :
todos/views.py
mysite/urls.py pour router /todos et /todos/<id> vers les vues :
mysite/urls.py
Exécuter et vérifier
Démarrez le serveur de développement :PATCH renvoie la ligne mise à jour :
http://localhost:3111/todos dans votre navigateur pour afficher la liste des tâches restantes. La réponse se présente comme suit :
guide_django puis public, et cliquez sur la table todos_todo. La table contient la ligne restante :
Paramètres PgBouncer pour Django
PgBouncer fonctionne en mode pool de connexions par transaction : chaque transaction, ou chaque statement hors transaction, peut s’exécuter sur une connexion Postgres différente. Voici les conséquences sur les paramètres ci-dessus et sur d’autres fonctionnalités de Django :DISABLE_SERVER_SIDE_CURSORS:QuerySet.iteratorlit les lignes par fragments au moyen d’un curseur côté serveur. Hors transaction, Django déclare le curseurWITH HOLDet récupère chaque fragment séparément ; via PgBouncer, une récupération peut donc atterrir sur une connexion Postgres qui ne possède pas le curseur et échouer aveccursor "_django_curs_..." does not exist. Cela ne se produit que lorsque d’autres clients utilisent le pool en même temps, si bien que le problème passe souvent inaperçu en développement. Lorsque ce paramètre est activé,.iterator()utilise un curseur classique et psycopg charge l’intégralité du résultat en mémoire. Pour lire une très grande table en streaming, exécutez ce code via la connexion directe sur le port5432, avec ce paramètre désactivé.CONN_MAX_AGE: par défaut (0), Django ouvre une nouvelle connexion pour chaque requête. L’ouverture d’une connexion TLS nécessite plusieurs allers-retours réseau, via PgBouncer comme en direct ; réutiliser les connexions accélère donc nettement les requêtes. Lors d’un test depuis un ordinateur portable avec quatre workers Gunicorn, définirCONN_MAX_AGEsur600a fait passer le temps de requête médian d’environ 670 ms à 95 ms. Via PgBouncer, une connexion Django ouverte ne monopolise pas de connexion Postgres entre les transactions ; les connexions persistantes n’épuisent donc pasmax_connections. AvecCONN_HEALTH_CHECKS, Django vérifie une connexion réutilisée au début de chaque requête et se reconnecte si elle a été fermée.- Pool de connexions : le pool intégré de Django (
"pool": TruedansOPTIONS, qui nécessitepip install "psycopg[binary,pool]") offre un gain similaire et fonctionne également via PgBouncer. Il exige queCONN_MAX_AGEsoit défini sur0et maintient au moins quatre connexions ouvertes par processus. Avec PgBouncer,CONN_MAX_AGEsuffit pour la plupart des applications ; envisagez le pool pour les serveurs multithreads ou ASGI. - Requêtes préparées : aucun paramètre n’est nécessaire. Par défaut, Django définit le
prepare_thresholdde psycopg surNoneet lie les paramètres de requête côté client ; il ne crée donc pas de requêtes préparées côté serveur. Si vous activez"server_side_binding": Trueet définissez"prepare_threshold"dansOPTIONS, les requêtes préparées fonctionnent aussi via PgBouncer, carpsycopg[binary]embarquelibpq18 et libère les statements à l’aide d’un messageCloseau niveau du protocole. Avec un psycopg compilé aveclibpq16 ou antérieur, la libération d’un statement au sein d’une transaction échoue avecprepared statement "_pg3_0" does not exist. Pour vérifier votre version, exécutezpython -c "import psycopg; print(psycopg.pq.version())". Il vous faut170000ou une version ultérieure. - État de session : un statement
SET, tel queSET search_path, ne s’applique qu’à la connexion Postgres qui l’a exécuté ; les requêtes suivantes risquent donc de ne pas le voir, alors que d’autres clients peuvent en hériter. PgBouncer rejette le paramètre Django"options": "-c search_path=..."avecunsupported startup parameter in options: search_path. Définissez plutôt des valeurs par défaut au niveau de la base de données, par exemple avecALTER DATABASE guide_django SET search_path = myschema, public;. N’utilisez pas l’optionassume_role, qui exécuteSET ROLE. La gestion native deTIME_ZONEpar Django et l’optionisolation_levelfonctionnent via PgBouncer.
PORT sur 5432 et supprimez DISABLE_SERVER_SIDE_CURSORS. Chaque worker Django conserve alors sa propre connexion Postgres ; veillez donc à ce que le nombre total de workers reste inférieur à max_connections.
Étapes suivantes
- Connexion : chaînes de connexion, PgBouncer et TLS
- Paramètres : paramètres de Postgres et de PgBouncer, comme
max_connections - Répliques en lecture : redirigez les lectures vers une réplique grâce à la prise en charge des bases de données multiples de Django
- Sécurité : listes d’accès IP et réseau privé