% Python 3.
D'autres forma!ons sont sur
h!ps://gayerie.dev
# » Accès aux bases de données (MySQL)
" Précédent Suivant !
Accès aux bases de
données (MySQL)
Python permet facilement d’interagir avec des
systèmes de bases de données rela!onnelles
(SGBDR). Ce"e facilité est en par!e due à la
défini!on d’une API standard d’accès aux
bases de données décrite par la PEP 249
(Python Database API Specifica!on v2.0).
Pour accéder à une base de données vous
devrez installer avec le module pip le
package pour vous connecter au SGBDR de
votre choix. La procédure de connexion peut
avoir des spécificités selon le SGBDR mais
l’interac!on avec la base reste standard.
Pour ce chapitre nous prendrons comme
exemple MySQL. Nous par!rons du principe
qu’il existe un serveur MySQL ac!f sur la
machine hôte et qu’il existe un schéma de
base de données appelé demo_python . Ce
schéma con!ent une unique table
utilisateur créée avec le script suivant :
drop table if exists utilisateur;
create table utilisateur (
id int primary key auto_increment,
nom varchar(100) not null,
score int default 0,
date_inscription date not null default
actif bool default FALSE
) engine=innoDB;
Installation du connecteur
MySQL
Pour vous connecter à une base de données
MySQL, vous devez installer le connecteur
MySQL qui est un paquet nommé
mysql-connector-python et qui est disponible
sur PyPI.
$ python -m pip install mysql-connector-python
Il est fortement recommandé d’installer le
connecteur dans un environnement virtuel
Python pour éviter d’entrer en conflit avec
d’autres projets qui u!liseraient déjà ce
connecteur. Pour en savoir plus, consultez le
chapitre consacré aux modules pip et venv.
Connexion à la base de
données
Le module mysql.connector fournit la
méthode connect qui permet de retourner
un objet qui représente la connexion vers la
base de données. Vous devez fournir les
paramètres host , user et password pour
donner l’adresse du SGBDR, le login et le mot
de passe de connexion. Vous pouvez
également fournir le paramètre database
pour indiquer quelle base de données vous
souhaitez u!liser.
import mysql.connector
db = mysql.connector.connect(
host="localhost",
user="login",
password="mot_de_passe",
database="demo_python"
)
# faire quelque chose d'utile avec la connexion
db.close()
La connexion à la base de données se
comporte comme un ges!onnaire de
ressource. Cela signifie que nous pouvons
employer la syntaxe with pour nous assurer
de fermer la connexion correctement. De
plus, il est u!le d’employer l’opérateur **
pour faire un unpack de dic!onnaire. Ainsi
nous pouvons représenter les paramètres de
connexion comme un simple dic!onnaire :
import mysql.connector
connection_params = {
'host': "localhost",
'user': "login",
'password': "mot_de_passe",
'database': "demo_python",
}
with mysql.connector.connect(**connection_param
# faire quelque chose d'utile avec la conne
pass
L’interac!on avec la base de données se fera à
travers un curseur. Cet objet permet à la fois
d’envoyer des requêtes et de consulter les
résultats quand il s’agit d’une requête de
consulta!on de données. Pour créer un
curseur, on appelle la méthode cursor() sur
la connexion. Il est recommandé de fermer un
curseur lorsqu’il n’est plus u!lisé. Comme un
curseur est également un ges!onnaire de
ressource, nous pouvons également employer
la syntaxe with :
import mysql.connector
connection_params = {
'host': "localhost",
'user': "login",
'password': "mot_de_passe",
'database': "demo_python",
}
with mysql.connector.connect(**connection_param
with db.cursor() as c:
# faire quelque chose d'utile avec le c
pass
Pour exécuter une requête, nous u!lisons la
méthode execute du curseur.
Insertion de données
L’inser!on de données se fait avec une
requête SQL de type insert .
1 with mysql.connector.connect(**connection_
2 with db.cursor() as c:
3 c.execute("insert into utilisateur
4 values ('david', 10, TR
5 db.commit()
Notez à la ligne 5, l’appel à la méthode
commit() de la connexion qui permet de
valider les modifica!ons. Si vous n’appelez
pas ce"e méthode, la transac!on avec la base
de données ne sera pas terminée et aucune
ligne ne sera insérée en base de données.
$ Note
Pour annuler toutes les modifica!ons
effectuées dans une transac!on, vous
pouvez appeler la méthode rollback()
de la connexion.
$ Note
Le connecteur MySQL fournit la propriété
non standard autocommit sur la
connexion. Si ce"e propriété vaut True
alors un commit est automa!quement fait
après chaque exécu!on de requête.
1 with mysql.connector.connect(**connectio
2 db.autocommit = True
3 with db.cursor() as c:
4 c.execute("insert into utilisate
5 values ('david', 10,
Il est souvent plus u!le de fournir les valeurs
à insérer sous la forme de paramètres. Il suffit
de remplacer les valeurs par %s dans la
requête et de passer en deuxième paramètre
à la méthodes execute un n-uplet avec les
valeurs.
request = """insert into utilisateur
(nom, score, actif)
values (%s, %s, %s)"""
params = ("david", 10, True)
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request, params)
db.commit()
Remarquez que le curseur se débrouille pour
conver!r les types Python en types SQL et
nous pouvons directement passer des chaînes
de caractères, des nombres, des valeurs
booléennes et même des dates.
import datetime
request = """insert into utilisateur
(nom, score, actif, date_inscripti
values (%s, %s, %s, %s)"""
params = ("david", 10, True, datetime.date.
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request, params)
db.commit()
La méthode executemany permet d’insérer
plusieurs lignes en passant un tableau pour la
valeur des paramètres :
request = """insert into utilisateur
(nom, score, actif)
values (%s, %s, %s)"""
params = [
("david", 10, True),
("laurence", 15, True),
("julien", 12, False)
]
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.executemany(request, params)
db.commit()
Si vous voulez connaître le nombre de lignes
insérées par l’exécu!on de la requête, vous
pouvez consulter la propriété rowcount du
curseur :
request = """insert into utilisateur
(nom, score, actif)
values (%s, %s, %s)"""
params = [
("david", 10, True),
("laurence", 15, True),
("julien", 12, False)
]
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.executemany(request, params)
db.commit()
print("Nombre de lignes insérées :"
Sélection de données
Pour récupérer des données depuis la base de
données, il suffit de passer un requête SQL de
type select en paramètre de la méthode
execute du curseur et ensuite d’appeler la
méthode fetchall() pour récupérer une liste
de n-uplets contenant les résultats.
request = "select id, nom, score, date_inscript
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request)
resultats = c.fetchall()
for utilisateur in resultats:
print(utilisateur)
L’exécu!on du code précédent affichera :
(1, 'david', 10, datetime.date(2021, 1, 17), 1)
(2, 'laurence', 15, datetime.date(2021, 1, 17),
(3, 'julien', 12, datetime.date(2021, 1, 17), 0
On voit que les résultats sont bien des n-
uplets et que le connecteur MySQL a réalisé
une conversion de type sauf pour la colonne
actif qui est un nombre au lieu d’être une
valeur booléenne. Cela n’est pas trop grave
car nous avons vu que True et False
peuvent presque être considérés comme les
nombres 1 et 0 .
$ Note
La conversion imparfaite pour les valeurs
booléennes ne vient pas de Python mais
de MySQL. En effet, le type BOOLEAN
n’existe pas vraiment en MySQL, il s’agit
plus d’un alias pour une colonne de type
en!er.
Récupérer les données en flux
La méthode fetchall() est simple à u!liser
mais peut poser un problème de performance.
En effet, ce"e méthode retourne une liste,
cela signifie que tous les résultats sont
récupérés de la base de données pour être
conver!s en n-uplets. S’il y a un nombre
important de lignes dans le résultat de la
requête, cela signifie que la liste peut être très
grande et avoir une empreinte mémoire
importante.
Parfois, on désire traiter directement la
donnée retournée et il n’est pas nécessaire de
la stocker dans une liste. Nous pouvons
améliorer notre code en optant pour l’appel à
la méthode fetchone() . Ce"e méthode
retourne un seul résultat sous la forme d’un n-
uplet. Lorsqu’il n’y a plus de résultat à lire, la
méthode retourne None . Nous pouvons
revoir notre code :
request = "select id, nom, score, date_inscript
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request)
while True:
utilisateur = c.fetchone()
if utilisateur is None:
break
print(utilisateur)
Le résultat du programme sera le même que
précédemment sauf que les résultats sont
extraits un à un et qu’aucune liste n’est créée.
$ Prudence
Si vous fermez un curseur avant d’avoir
extrait tous les résultats, vous ob!endrez
une excep!on. N’u!lisez pas la méthode
fetchone() pour ne retourner que le
premier résultat, elle n’a pas été conçue
pour cela. Son rôle est de perme"re de
retourner tous les résultats d’une requête
mais un à un.
Si vous voulez limiter le nombre de
résultats retournés par une requête,
u!lisez l’instruc!on SQL limit :
select id, nom, score, date_inscription,
La méthode fetchmany() est un compromis
entre fetchone() et fetchall() . En effet, si
fetchall() peut avoir un impact sur la
consomma!on mémoire, fetchone() peut
avoir un impact sur les performances car les
aller-retour entre le programme et la base de
données ont un coût en terme de temps.
La méthode fetchmany() prend en paramètre
le nombre maximum de résultats qu’il faut
aller chercher lors de ce"e appel. Lorsqu’il n’y
a plus de résultat, la méthode retourne une
liste vide.
request = "select id, nom, score, date_inscript
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request)
while True:
resultats = c.fetchmany(10)
if not resultats:
break
for utilisateur in resultats:
print(utilisateur)
Si ce code paraît difficile à écrire, il est assez
facile de créer une fonc!on pour simplifier
une fois pour toute notre travail :
def fetch_from_database(cursor, request, callba
"""
Utilise le cursor pour exécuter la requ
callback est une méthode qui est appelé
Elle recevra en paramètre le tuple des
chunck_size indique le nombre max de ré
"""
cursor.execute(request)
while True:
resultats = c.fetchmany(chunck_size
if not resultats:
break
for colonnes in resultats:
callback(colonnes)
Et le programme devient alors :
request = "select id, nom, score, date_inscript
with mysql.connector.connect(**connection_param
with db.cursor() as c:
fetch_from_database(c, request, print
Utilisation de paramètres dans
les conditions where
Comme pour les requêtes d’inser!on, il est
u!le de spécifier des paramètres dans une
requête SELECT pour les valeurs d’une clause
where .
$ Astuce
Ce mécanisme nous prémunit de
l’injec!on SQL car la structure de la
requête est fournie quelle que soit la
valeur des paramètres.
Les paramètres sont spécifiés par %s dans la
requête et leurs valeurs sont données par un
n-uplet passé comme second paramètre à la
méthode execute :
request = "select id, nom, score, date_inscript
from utilisateur where nom = %s"
params = ("david",)
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request, params)
resultats = c.fetchall()
for utilisateur in resultats:
print(utilisateur)
Modification des données
Toutes les autres requêtes SQL de
modifica!on suivent le même principe que les
requêtes d’inser!on. On u!lise la méthode
execute d’un curseur. Il faut appeler la
méthode commit() de la connexion pour
valider la transac!on. Enfin, il est possible de
passer des paramètres à la requête et de
connaître le nombre de lignes impactées par
la requête grâce à la propriété rowcount .
request = "update utilisateur \
set actif = %s \
where nom = %s"
params = (False, "david")
with mysql.connector.connect(**connection_param
with db.cursor() as c:
c.execute(request, params)
db.commit()
print("Nombre de lignes mises à jour :"
" Précédent Suivant !
h"ps://gayerie.dev
Ce"e œuvre est mise à disposi!on selon
les termes de la Licence Crea!ve
Commons A"ribu!on - Partage dans les
Mêmes Condi!ons 3.0 France