Skip to content

1 4 8 4 programmation

Une étude de cas ⅘ : programmation (Python)

Supports complémentaires

Voici maintenant quelques exemples de programmes accédant à notre base de données. Nous reprenons notre hypothèse d´une base nommée ´´Messagerie", gérée par un SGBD relationnel (disons, ici, MySQL). Notre utilisatrice est Athénaïs: elle va écrire quelques scripts Python pour exécuter ses requêtes (Fig 59).

Note
Le choix de Python est principalement motivé par la concision et la simplicité. On trouverait à peu près l´équivalent des programmes ci-dessous dans n´importe quel langage (notamment en Java,avec l´API JDBC). Par ailleurs, l´interface Python illustrée ici est standard pour tous les SGBD et nos scripts fonctionneraient sans doute à peu de chose près avec Postgres ou un autre.

Nos scripts sont des programmes clients, qui peuvent s´exécuter sur une machine, se connecter par le réseau au serveur de données, auquel ils transmettent des commandes (principalement des requêtes SQL). Nous sommes dans l´architecture très classique de la Fig. 59.

figure59

Fig. 59. Architecture d'un programme dialoguant avec un serveur

Un programme de lecture

Pour établir une connexion, tout programme client doit fournir au moins 4 paramètres: l´adresse de la machine serveur (une adresse IP, ou le nom de la machine), le nom et le mot de passe de l´utilisateur qui se connecte, et le nom de la base. On fournit souvent également des options qui règlent certains détails de communication entre le client et le serveur. Voici donc la connexion à MySQL avec notre programme Python.

connexion = pymysql.connect
        ('localhost', 
         'athénaïs', 
         'motdepasse', 
         'Messagerie',
          cursorclass=pymysql.cursors.DictCursor)

Ici, on se connecte à la machine locale sous le compte d´Athénaïs, et on accède à la base Messagerie. Le dernier paramètre est une option cursorClass qui indique que les données (nuplets) retournés par le serveur seront représentés par des dictionnaires Python.

Note
Un dictionnaire est une structure qui associe des clés (les noms des attributs) et des valeurs. Cette structure est bien adaptée à la représentation des nuplets.

Un curseur est créé simplement de la manière suivante:

curseur = connexion.cursor()

Une fois que l´on a créé un curseur, on s´en sert pour exécuter une requête.

curseur.execute("select * from Contact")

À ce stade, rien n´est récupéré côté client. Le serveur a reçu la requête, a créé le plan d´exécution et se tient prêt à fournir des données au client dès que ce dernier les demandera. Comme nous l´avons vu dans le chapitre sur la programmation, un curseur permet de parcourir le résultat d´une requête. Ici ce résultat est obtenu globalement avec la commande fetchAll() (on pourrait utiliser fetchOne()) pour récupérer les nuplets un par un). Le code Python pour parcourir tout le résultat est donc:

for contact in curseur.fetchall():
   print(contact['prénom'], contact['nom'])

La boucle affecte, à chaque itération, le nuplet courant à la variable contact. Cette dernière est donc un dictionnaire dont chaque entrée associe le nom de l´attribut et sa valeur.

Et voilà. Pour résumer, voici le programme complet, qui est donc remarquablement concis.

import pymysql
import pymysql.cursors

connexion = pymysql.connect('localhost', 'athénaïs', 
                     'motdepasse', 'Messagerie',
                     cursorclass=pymysql.cursors.DictCursor)

curseur = connexion.cursor()
curseur.execute("select * from Contact")

for contact in curseur.fetchall():
    print(contact['prénom'], contact['nom'])

Bien entendu, il faudrait ajouter un petit travail d´ingénierie pour ne pas donner les paramètres de connexion sous forme de constante mais les récupérer dans un fichier de configuration, et ajouter le traitement des erreurs (traiter par exemple un refus de connexion).

Une transaction

Notre second exemple montre une transaction qui sélectionne tous les messages non encore envoyés, les envoie, et marque ces messages en leur affectant la date d´envoi. Voici le programme complet, suivi de quelques commentaires.

import pymysql
import pymysql.cursors
from datetime import datetime

connexion = pymysql.connect('localhost', 'athénaïs', 
                 'motdepasse', 'Messagerie',
                 cursorclass=pymysql.cursors.DictCursor)

# Tous les messages non envoyés
messages = connexion.cursor()
messages.execute("select * from Message where dateEnvoi is null")
for message in messages.fetchall():
    # Marquage du message
    connexion.begin()
    maj = connexion.cursor()
    maj.execute ("Update Message set dateEnvoi='2018-12-31' "
        + "where idMessage=%s", message['idMessage'])

    # Ici on envoie les messages à tous les destinataires
    envois = connexion.cursor()
    envois.execute("select * from Envoi as e, Contact as c "
           +" where e.idDestinataire=c.idContact "
           + "and  e.idMessage = %s", message['idMessage'])
    for envoi in envois.fetchall():
        mail (envoi['email'], message['contenu')

    connexion.commit()

Donc, ce programme effectue une boucle sur tous les messages qui n´ont pas de date d´envoi (lignes 10-12). À chaque itération, le cursor affecte une variable message.

Chaque passage de la boucle donne lieu à une transaction, initiée avec connexion.begin() et conclue avec connexion.commit(). Cette transaction effectue en tout et pour tout une seule mise à jour, celle affectant la date d´envoi au message (il faudrait bien entendu trouver la date du jour, et ne pas la mettre "en dur").

Dans la requête update (lignes 16-17), notez qu´on a séparé la requête SQL et ses paramètres (ici, l´identifiant du message). Cela évite de construire la requête comme une chaîne de caractères. On ouvre ensuite un second curseur (lignes 20-24), sur les destinataires du message, et on envoie ce dernier.

Une remarque importante: les données traitées (message et destinataires) pourraient être récupérées en une seule requête SQL par une jointure. Mais le format du résultat (une table dans laquelle le message est répété avec chaque destinataire) ne convient pas du tout à la structure du programme dont la logique consiste à récupérer d´abord le message, puis à parcourir les envois, en deux requêtes. En d´autres termes, dans ce type de programme (très courant), SQL est sous-utilisé. Nous revenons sur ce point dans la dernière session.