transfer from private server/git server
This commit is contained in:
93
web/db.py
Normal file
93
web/db.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
db.py — Module centralisé d'accès à la base de données MariaDB.
|
||||
|
||||
Ce module est le SEUL point de contact avec PyMySQL dans l'application.
|
||||
Il expose un gestionnaire de connexion/curseur réutilisable par tous les
|
||||
modèles de la couche models/, avec :
|
||||
- gestion des erreurs et rollback automatique,
|
||||
- reconnexion en cas de perte de connexion (ping avant chaque emprunt),
|
||||
- curseurs en mode dictionnaire (résultats accessibles par nom de colonne).
|
||||
|
||||
Toutes les requêtes exécutées via ce module DOIVENT utiliser des paramètres
|
||||
préparés (placeholders %s) — jamais de concaténation de chaînes — afin
|
||||
d'éliminer tout risque d'injection SQL (Bloc 1 - C4, C8 ; OWASP A03:2021).
|
||||
"""
|
||||
|
||||
import logging
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
|
||||
logger = logging.getLogger("gesthub.db")
|
||||
|
||||
|
||||
class Database:
|
||||
"""Petite couche d'abstraction au-dessus de PyMySQL (pas d'ORM)."""
|
||||
|
||||
def __init__(self, host, port, user, password, database):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.database = database
|
||||
|
||||
def _connect(self):
|
||||
return pymysql.connect(
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
user=self.user,
|
||||
password=self.password,
|
||||
database=self.database,
|
||||
cursorclass=pymysql.cursors.DictCursor,
|
||||
autocommit=False,
|
||||
charset="utf8mb4",
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def cursor(self, commit=False):
|
||||
"""Context manager fournissant un curseur et gérant transaction/erreurs.
|
||||
|
||||
Usage :
|
||||
with db.cursor(commit=True) as cur:
|
||||
cur.execute("INSERT INTO annonces (titre) VALUES (%s)", (titre,))
|
||||
"""
|
||||
conn = self._connect()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
yield cur
|
||||
if commit:
|
||||
conn.commit()
|
||||
except pymysql.MySQLError:
|
||||
conn.rollback()
|
||||
logger.exception("Erreur SQL — transaction annulée (rollback)")
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def fetch_all(self, query, params=None):
|
||||
with self.cursor() as cur:
|
||||
cur.execute(query, params or ())
|
||||
return cur.fetchall()
|
||||
|
||||
def fetch_one(self, query, params=None):
|
||||
with self.cursor() as cur:
|
||||
cur.execute(query, params or ())
|
||||
return cur.fetchone()
|
||||
|
||||
def execute(self, query, params=None):
|
||||
"""INSERT/UPDATE/DELETE avec commit automatique. Retourne lastrowid."""
|
||||
with self.cursor(commit=True) as cur:
|
||||
cur.execute(query, params or ())
|
||||
return cur.lastrowid
|
||||
|
||||
|
||||
def init_db(config):
|
||||
"""Fabrique une instance Database à partir d'un objet de configuration."""
|
||||
return Database(
|
||||
host=config.DB_HOST,
|
||||
port=config.DB_PORT,
|
||||
user=config.DB_USER,
|
||||
password=config.DB_PASSWORD,
|
||||
database=config.DB_NAME,
|
||||
)
|
||||
Reference in New Issue
Block a user