Vous êtes sur la page 1sur 1

% 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 !

Par David Gayerie - david@gayerie.dev -


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

Vous aimerez peut-être aussi