« Programmation Python/Gestion d'une base de données » : différence entre les versions

Contenu supprimé Contenu ajouté
Aucun résumé des modifications
Ligne 56 :
Vous devez ensuite créer une instance (un objet) de la classe gadfly :
 
<source lang=python>
<pre>
import gadfly
baseDonn = gadfly.gadfly()
</presource>
 
L'objet <code>baseDonn</code> ainsi créé est votre moteur de base de données local, lequel effectuera la plupart de ses opérations en mémoire vive. Ceci permet une exécution très rapide des requêtes.
Ligne 65 :
Pour créer la base de données proprement dite, il faut employer la méthode startup de cet objet :
 
<source lang=python>
<pre>
baseDonn.startup("mydata","E:/Python/essais/gadfly")
</presource>
 
Le premier paramètre transmis, <code>mydata</code>, est le nom choisi pour la base de données (vous pouvez évidemment choisir un autre nom !). Le second paramètre est le répertoire où l'on souhaite installer cette base de données. (Ce répertoire doit avoir été créé au préalable, et toute base de données de même nom qui préexisterait dans ce répertoire est écrasée sans avertissement).
Ligne 77 :
Afin de pouvoir transmettre vos requêtes SQL à l'objet <code>baseDonn</code> , vous devez cependant mettre en œuvre un ''curseur''. Il s'agit d'une sorte de tampon mémoire intermédiaire, destiné à mémoriser temporairement les données en cours de traitement, ainsi que les opérations que vous effectuez sur elles, avant leur transfert définitif dans de vrais fichiers. Cette technique permet donc d'annuler si nécessaire une ou plusieurs opérations qui se seraient révélées inadéquates (Vous pouvez en apprendre davantage sur ce concept en consultant l'un des nombreux manuels qui traitent du langage SQL). Veuillez à présent examiner le petit script ci-dessous, et noter que les requêtes SQL sont des chaînes de caractères, prises en charge par la méthode execute de l'objet ''curseur'' :
 
<source lang=python>
<pre>
cur = baseDonn.cursor()
cur.execute("create table membres (age integer, nom varchar, taille float)")
Ligne 84 :
cur.execute("Insert Into Membres(Age, Nom, Taille) Values (18,'Forcas',1.69)")
baseDonn.commit()
</presource>
 
La première des lignes ci-dessus crée l'objet curseur <code>cur</code>. Les chaînes de caractères comprises entre guillemets dans les 4 lignes suivantes contiennent des requêtes SQL très classiques. ''Notez bien que le langage SQL ne tient aucun compte de la casse des caractères'' : vous pouvez encoder vos requêtes SQL indifféremment en majuscules ou en minuscules (ce qui n'est pas le cas pour les instructions Python environnantes, bien entendu !)
Ligne 100 :
L'accès à une base de données existante ne nécessite que deux lignes de code :
 
<source lang=python>
<pre>
import gadfly
baseDonn = gadfly.gadfly("mydata","E:/Python/essais/gadfly")
</presource>
 
Ces deux lignes suffisent en effet pour transférer en mémoire vive les tables contenues dans les fichiers enregistrés sur disque. La base de données peut désormais être interrogée et modifiée :
 
<source lang=python>
<pre>
cur = baseDonn.cursor()
cur.execute("select * from membres")
print cur.pp()
</presource>
 
La première de ces trois lignes ouvre un curseur. La requête émise dans la seconde ligne demande la sélection d'un ensemble d'enregistrements, qui seront transférés de la base de données au curseur. Dans le cas présent, la sélection n'en n'est pas vraiment une : on y demande en effet d'extraire tous les enregistrements de la table membres (le symbole * est fréquemment utilisé en informatique avec la signification « tout » ou « tous »).
Ligne 119 :
Si vous préférez contrôler vous-même la mise en page des informations, il vous suffit d'utiliser à sa place la méthode <code>fetchall()</code>, laquelle renvoie une liste de tuples. Essayez par exemple :
 
<source lang=python>
<pre>
for x in cur.fetchall():
print x, x[0], x[1], x[2]
</presource>
 
Vous pouvez bien entendu ajouter des enregistrements supplémentaires :
 
<source lang=python>
<pre>
cur.execute("Insert Into Membres(Age, Nom, Taille) Values (19,'Ricard',1.75)")
</presource>
 
Pour modifier un ou plusieurs enregistrements, exécutez une requête du type :
 
<source lang=python>
<pre>
cur.execute("update membres set nom ='Gerart' where nom='Ricard'")
</presource>
 
Pour supprimer un ou plusieurs enregistrements, utilisez une requête telle que :
 
<source lang=python>
<pre>
cur.execute("delete from membres where nom='Gerart'")
</presource>
 
Si vous effectuez toutes ces opérations à la ligne de commande de Python, vous pouvez en observer le résultat à tout moment en effectuant un ''pretty print'' comme expliqué plus haut. Étant donné que toutes les modifications apportées au curseur se passent en mémoire vive, rien n'est enregistré définitivement tant que vous n'exécutez pas l'instruction <code>baseDonn.commit()</code>.
Ligne 146 :
Vous pouvez donc annuler toutes les modifications apportées depuis le commit() précédent, en refermant la connexion à l'aide de l'instruction :
 
<source lang=python>
<pre>
baseDonn.close()
</presource>
 
=== Recherches dans une base de données ===
Ligne 191 :
<li>
(Création de la base de données "musique") :
<source lang=python>
<pre>
import gadfly
 
Ligne 234 :
 
connex.commit()
</presource>
</li>
</ol>
Ligne 241 :
Commencez à remplir la table <code>compositeurs</code> avec les données qui suivent (... et profitez de cette occasion pour faire la preuve des compétences que vous maîtrisez déjà, en écrivant un petit script pour vous faciliter l'entrée des informations : une boucle s'impose !)
 
<source lang=python>
<pre>
comp a_naiss a_mort
 
Ligne 252 :
Chopin 1810 1849
Bach 1685 1750
</presource>
 
Dans la table oeuvresœuvres, entrez les données suivantes :
 
<source lang=python>
<pre>
comp titre duree interpr
 
Ligne 272 :
Mozart Concerto piano N°22 35 S. Richter
Beethoven Concerto piano N°3 37 S. Richter
</presource>
 
 
Ligne 345 :
Pour terminer ce chapitre, nous allons vous proposer dans les pages qui suivent un exemple de réalisation concrète. Il ne s'agira pas d'un véritable logiciel (le sujet exigerait qu'on lui consacre un ouvrage spécifique), mais plutôt d'une ébauche d'analyse, destinée à vous montrer comment vous pouvez « penser comme un programmeur » lorsque vous abordez un problème complexe.
 
Les techniques que nous allons mettre en oeuvreœuvre ici sont de simples suggestions, dans lesquelles nous essayerons d'utiliser au mieux les outils que vous avez découverts au cours de votre apprentissage dans les chapitres précédents, à savoir : les structures de données de haut niveau (listes et dictionnaires), et la programmation par objets. Il va de soi que les options retenues dans cet exercice restent largement critiquables : vous pouvez bien évidemment traiter les mêmes problèmes en utilisant des approches différentes.
 
Notre objectif concret est d'arriver à réaliser rapidement un client rudimentaire, capable de dialoguer avec un « vrai » serveur de bases de données tel que MySQL. Nous voudrions que notre client reste un petit utilitaire très généraliste : qu'il soit capable de mettre en place une petite base de données comportant plusieurs tables, qu'il puisse servir à produire des enregistrements pour chacune d'elles, qu'il permette de tester le résultat de requêtes SQL basiques.
Ligne 409 :
 
 
<source lang=python>
<pre>
class Glob:
"""Espace de noms pour les variables et fonctions <pseudo-globales>"""
Ligne 429 :
('duree', "i", "durée (en minutes)"),
('interpr', 30, "interprète principal")]}
</presource>
 
Le dictionnaire d'application décrivant la structure de la base de données est contenu dans la variable <code>Glob.dicoT</code>.
Ligne 447 :
Les objets-interfaces que nous voulons construire seront similaires aux objets-fichiers que nous avons abondamment utilisés pour la gestion des fichiers au chapitre 9. Vous vous rappelez par exemple que nous ouvrons un fichier en créant un objet-fichier, à l'aide de la fonction-fabrique <code>open()</code>. D'une manière similaire, nous ouvrirons la communication avec la base de données en commençant par créer un objet-interface à l'aide de la classe <code>GestionBD()</code>, ce qui établira la connexion. Pour lire ou écrire dans un fichier ouvert, nous utilisons diverses méthodes de l'objet-fichier. D'une manière analogue, nous effectuerons nos opérations sur la base de données par l'intermédiaire des diverses méthodes de l'objet-interface.
 
<source lang="python" line>
1.import MySQLdb, sys
2.from dict_app import *
 
3.
4.class GestionBD:
5. """Mise en place et interfaçage d'une base de données MySQL"""
6. def __init__(self, dbName, user, passwd, host, port =3306):
7. "Établissement de la connexion - Création du curseur"
8. try:
9. self.baseDonn = MySQLdb.connect(db =dbName,
10. user =user, passwd =passwd, host =host, port =port)
11. except Exception, err:
12. print 'La connexion avec la base de données a échoué :\n'\
13. 'Erreur détectée :\n%s' % err
14. self.echec =1
15. else:
16. self.cursor = self.baseDonn.cursor() # création du curseur
17. self.echec =0
 
18.
19. def creerTables(self, dicTables):
20. "Création des tables décrites dans le dictionnaire <dicTables>."
21. for table in dicTables: # parcours des clés du dict.
22. req = "CREATE TABLE %s (" % table
23. pk =''
24. for descr in dicTables[table]:
25. nomChamp = descr[0] # libellé du champ à créer
26. tch = descr[1] # type de champ à créer
27. if tch =='i':
28. typeChamp ='INTEGER'
29. elif tch =='k':
30. # champ 'clé primaire' (incrémenté automatiquement)
31. typeChamp ='INTEGER AUTO_INCREMENT'
32. pk = nomChamp
33. else:
34. typeChamp ='VARCHAR(%s)' % tch
35. req = req + "%s %s, " % (nomChamp, typeChamp)
36. if pk == '':
37. req = req[:-2] + ")"
38. else:
39. req = req + "CONSTRAINT %s_pk PRIMARY KEY(%s))" % (pk, pk)
40. self.executerReq(req)
 
41.
42. def supprimerTables(self, dicTables):
43. "Suppression de toutes les tables décrites dans <dicTables>"
44. for table in dicTables.keys():
45. req ="DROP TABLE %s" % table
46. self.executerReq(req)
47. self.commit() # transfert -> disque
 
48.
49. def executerReq(self, req):
50. "Exécution de la requête <req>, avec détection d'erreur éventuelle"
51. try:
52. self.cursor.execute(req)
53. except Exception, err:
54. # afficher la requête et le message d'erreur système :
55. print "Requête SQL incorrecte :\n%s\nErreur détectée :\n%s"\
56. % (req, err)
57. return 0
58. else:
59. return 1
 
60.
61. def resultatReq(self):
62. "renvoie le résultat de la requête précédente (un tuple de tuples)"
63. return self.cursor.fetchall()
 
64.
65. def commit(self):
66. if self.baseDonn:
67. self.baseDonn.commit() # transfert curseur -> disque
 
68.
69. def close(self):
70. if self.baseDonn:
71. self.baseDonn.close()
</source>
 
Ligne 548 :
Dans une application véritable, ce formulaire trop simpliste devrait certainement être fortement remanié, et il prendrait vraisemblablement la forme d'une fenêtre spécialisée, dans laquelle les champs d'entrée et leurs libellés pourraient encore une fois être générés de manière automatique. Nous ne prétendons donc pas qu'il constitue un bon exemple, mais nous voulons simplement vous montrer comment vous pouvez automatiser sa construction dans une large mesure. Tâchez de réaliser vos propres formulaires en vous servant de principes semblables.
 
<source lang="python" line>
<pre>
class Enregistreur: #1
"""classe pour gérer l'entrée d'enregistrements divers""" #2
def __init__(self, bd, table): #3
self.bd =bd #4
self.table =table #5
self.descriptif =Glob.dicoT[table] # descriptif des champs #6
 
#7
def entrer(self): #8
"procédure d'entrée d'un enregistrement entier" #9
champs ="(" # ébauche de chaîne pour les noms de champs #10
valeurs ="(" # ébauche de chaîne pour les valeurs #11
# Demander successivement une valeur pour chaque champ : #12
for cha, type, nom in self.descriptif: #13
if type =="k": # on ne demandera pas le n° d'enregistrement #14
continue # à l'utilisateur (numérotation auto.) #15
champs = champs + cha + "," #16
val = raw_input("Entrez le champ %s :" % nom)
if type #17=="i":
if type =="i": valeurs = valeurs + val #18+","
else:
valeurs = valeurs + val +"," #19
else: valeurs = valeurs + "'%s'," % #20(val)
 
valeurs = valeurs + "'%s'," % (val) #21
champs = champs[:-1] + ")" # supprimer la dernière virgule,
#22
champsvaleurs = champsvaleurs[:-1] + ")" # supprimerajouter laune dernière virgule, #23parenthèse
req ="INSERT INTO %s %s VALUES %s" % (self.table, champs, valeurs) #25
valeurs = valeurs[:-1] + ")" # ajouter une parenthèse #24
self.bd.executerReq(req) #26
req ="INSERT INTO %s %s VALUES %s" % (self.table, champs, valeurs) #25
 
self.bd.executerReq(req) #26
ch =raw_input("Continuer (O/N) ? ") #28
#27
if ch.upper() == "O":
ch =raw_input("Continuer (O/N) ? ") #28
return 0
if ch.upper() == "O": #29
else:
return 0 #30
return 1
else: #31
</source>
return 1 #32
</pre>
 
;Commentaires
Ligne 605 :
Le script qui suit est celui d'une petite application destinée à tester les classes décrites dans les pages qui précèdent. Libre à vous de la perfectionner, ou alors d'en écrire une autre tout à fait différente !
 
<source lang="python" line>
<pre>
1.###### Programme principal : #########
 
2.
3.# Création de l'objet-interface avec la base de données :
4.bd = GestionBD(Glob.dbName, Glob.user, Glob.passwd, Glob.host)
5.if bd.echec:
6. sys.exit()
7.
8.while 1:
9. print "\nQue voulez-vous faire :\n"\
10. "1) Créer les tables de la base de données\n"\
11. "2) Supprimer les tables de la base de données ?\n"\
12. "3) Entrer des compositeurs\n"\
13. "4) Entrer des oeuvres\n"\
14. "5) Lister les compositeurs\n"\
15. "6) Lister les oeuvres\n"\
16. "7) Exécuter une requête SQL quelconque\n"\
17. "9) terminer ? Votre choix :",
18. ch = int(raw_input())
19. if ch ==1:
20. # création de toutes les tables décrites dans le dictionnaire :
21. bd.creerTables(Glob.dicoT)
22. elif ch ==2:
23. # suppression de toutes les tables décrites dans le dic. :
24. bd.supprimerTables(Glob.dicoT)
25. elif ch ==3 or ch ==4:
26. # création d'un <enregistreur> de compositeurs ou d'oeuvres :
27. table ={3:'compositeurs', 4:'oeuvres'}[ch]
28. enreg =Enregistreur(bd, table)
29. while 1:
30. if enreg.entrer():
31. break
32. elif ch ==5 or ch ==6:
33. # listage de tous les compositeurs, ou toutes les oeuvres :
34. table ={5:'compositeurs', 6:'oeuvres'}[ch]
35. if bd.executerReq("SELECT * FROM %s" % table):
36. # analyser le résultat de la requête ci-dessus :
37. records = bd.resultatReq() # ce sera un tuple de tuples
38. for rec in records: # => chaque enregistrement
39. for item in rec: # => chaque champ dans l'enreg.
40. print item,
41. print
42. elif ch ==7:
43. req =raw_input("Entrez la requête SQL : ")
44. if bd.executerReq(req):
45. print bd.resultatReq() # ce sera un tuple de tuples
46. else:
47. bd.commit()
48. bd.close()
49. break
</presource>
 
;Commentaires