Attaquer une BDD Firebird sous Delphi édition personnelle grâce à Python

Guillaume Duriaud

Attaquer une base de données Firebird sous Delphi 7/2005 édition personnelle (win32).
Utilisation de la bibliothèque PythonForDelphi ♪

- Titre : Attaquer une BDD Firebird sous Delphi édition personnelle grâce à Python

- Auteur : Guillaume Duriaud

- Parution : 9 juillet 2005

- Mise à jour : 7 novembre 2006

- Licence : Copyright ® 2005-2006 Guillaume Duriaud. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Installation des composants

- II-A. Installation de la bibliothèque PythonForDelphi

- II-B. Installation de Firebird 2.0

- II-C. Installation des dépendances Python

- II-D. Installation du fichier FBBase.py

- II-E. Tableau récapitulatif

- III. Python et les bases de données

- III-A. L'API 2.0 de Python pour les bases de données (DBAPI)

- III-A-1. Introduction

- III-A-2. La fonction connect

- III-A-3. Classe Connection

- III-A-4. Classe Cursor

- III-B. Le module FBBase.py - interface avec la BDD Firebird

- III-B-1. Introduction

- III-B-2. L'objet TFBConnection

- III-B-3. L'objet TFBCursor

- IV. Programmation sous Delphi

- IV-A. Connection à la base de données et initialisation des objets

- IV-A-1. PythonEngine

- IV-A-2. Initialisation des objets

- IV-A-3. Connexion à la base de données

- IV-B. Utilisation de requêtes SQL

- IV-B-1. Exemple 1

- IV-B-2. Exemple 2

- V. Pour aller plus loin

- V-A. Interface pyBase.py

- V-B. Déploiement de vos applications

- V-B-1. Interface pyBase.py

- V-B-2. Dépendances Python

- V-B-3. Ajout de nouvelles bases de données

- VI. Téléchargement

- VII. Conclusion

I - Introduction

L'absence de composants natifs aux bases de données sous une version personnelle est un des freins importants à ceux qui souhaitent s'initier à Delphi. Une solution consiste à passer par un autre langage. Ce langage est Python, un langage objet, open source et interprété très puissant. Les composants PythonForDelphi permettent d'utiliser des objets Python sous Delphi.

Ce tutoriel est destiné aux programmeurs Delphi d'une version personnelle qui souhaitent utiliser malgré tout des bases de données. Sachez qu'aucune connaissance en Python n'est exigée. Tout le code que vous écrirez sera exclusivement en Delphi.

En revanche, on ne pourra utiliser les composants du style (DBListBox, DBComboBox, DBGrid…) vu que l'on travaille sous une version personnelle de Delphi. Tout se fera par requête SQL et exploitation de ces requêtes.
Écrit en juillet 2005, ce tutoriel a été revu en novembre 2006 pour être compatible avec les dernières versions des logiciels/bibliothèques actuellement disponible (notamment python 2.5 et Firebird 2.0)

II - Installation des composants

Vous pouvez télécharger le source complet des exemples ici. Il a été écrit sous Delphi 7 édition personnelle, mais a également été testé sous Delphi 2005 édition personnelle (win 32). Ce fichier contient :

- le source de l'exemple ;

- l'exécutable de l'exemple ;

- le runtime FBruntimePython contenant le moteur Python et les dépendances pour Firebird ;

- le fichier FBBase.py, interface permettant de manipuler des bases Firebird.

Aperçu de l'interface de l'exemple :

II-A - Installation de la bibliothèque PythonForDelphi

La première chose est d'installer la bibliothèque PythonForDelphi. L'installation est très facile. Il suffit de récupérer l'installateur ici. La version utilisée pour ce tutoriel est la version 3.32. Ensuite lancez l'exécutable et suivez les instructions.
Le répertoire par défaut est c:\Program Files\PythonForDelphi :

Arrivé à cette fenêtre,

cliquez tout d'abord sur le bouton Edit Definition.inc, commentez la ligne 41 {$DEFINE PREFER_UNICODE} // this will convert a variant containing an OleStr to a UniCode Python string., puis enregistrez le fichier. Vous pourrez toujours remodifier ce fichier plus tard que vous retrouverez dans le répertoire ./Components/Sources/Core. C'est également ce fichier qui vous permettra de compiler vos programmes Delphi avec différentes versions de Python.
Sélectionnez ensuite votre version de Delphi et la version 2.5 de Python puis cliquez sur le bouton Compile. Cela installera les composants dans l'IDE de Delphi. Si vous avez une version personnelle de Delphi, une erreur apparaîtra dans les logs,

car tous les composants n'auront pas été installés (certains nécessitant une version professionnelle).
Arrivé à la fenêtre suivante, cliquez directement sur Cancel.

En ouvrant l'IDE de Delphi 7, vous aurez un nouvel onglet qui contiendra au moins ces icônes :

Et sous Delphi 2005 :

Présentation succincte des composants. Beaucoup sont encore présents pour raison historique.

Composant

Description

PythonGUIInputOutput

Ce composant permet de rediriger la sortie de l'exécution d'un code Python vers un TMemo ou un TRichEdit.

AtomPythonEngine

Composant déprécié à partir de Delphi 6. Utiliser le composant PythonEngine

PythonEngine

Moteur Python - permet d'utiliser d'autres composants de PythonForDelphi

PythonInputOutput

Ce composant permet de rediriger la sortie de l'exécution d'un code Python vers ce que l'on veut

PythonType

Ce composant permet de créer un nouveau type Objet.

PythonModule

Ce composant permet de créer un nouveau module. On peut également s'en servir pour stocker son propre code python qui sera inclus dans le .exe pour ne pas avoir à distribuer des fichiers .py en annexe.

PythonDelphiVar

Ce composant permet de créer des variables Python qui seront également accessibles depuis Delphi

PyDelphiWrapper

Ce composant (apparaissant dans la version 3.29) permet d'exporter des variables Delphi

PythonDatabase

Ce composant (présent avec version commerciale de Delphi) implémente les unités VCL DB et DBTables pour l'accès à une base de données

II-B - Installation de Firebird 2.0

Le serveur utilisé était d'une version 2.0RC5. Vous pouvez le télécharger directement sur le site de Firebird.

II-C - Installation des dépendances Python

Vous avez à ce stade deux possibilités pour votre développement et pour le déploiement du logiciel.
Vous pouvez opter pour l'installation complète du langage Python et des librairies utiles (comptez 30 Mo en tout) ou bien simplement un runtime (4 Mo) à installer dans le répertoire de l'application.
Pour l'installation, c'est très simple, il suffit de récupérer les installateurs autoextractibles disponibles sur les sites dont vous trouverez les adresses ici.
Langages et librairies utilisés lors du développement de ce tutoriel :
- Python 2.5;
- kinterbasdb 3.2.0.
Si vous optez pour le runtime, il suffit de décompresser l'archive disponible ici dans le répertoire de l'application. Ce runtime (assez compliqué à créer) est figé dans le sens où il a été construit à partir de Python 2.5 et kinterbasdb 3.2.0 et qu'il n'est pas soumis à l'arrivée de nouvelles versions de ces librairies. Cependant des mises à jour seront faites si nécessaire.
Si vous pensez modifier les sources Python, pensez à installer Python 2.5 qui fournit un éditeur adapté à la gestion des sources Python.
Si de plus, vous souhaitez utiliser d'autres objets Python avec Delphi, il est préférable d'installer le langage Python plutôt que d'utiliser le runtime que je vous fournis.

II-D - Installation du fichier FBBase.py

Il suffit de copier ce fichier à la racine du répertoire de l'application. Ce fichier correspond à l'interface Python avec la base de données Firebird.

II-E - Tableau récapitulatif

Développement (version Runtime)

Développement (version Full)

Déploiement (version Runtime)

Déploiement (version Full)

Delphi 7/2005

Delphi 7/2005

Exécutable de l'application

Exécutable de l'application

PythonForDelphi 3.32

PythonForDelphi 3.32

Firebird 2.0

Firebird 2.0

Firebird 2.0

Firebird 2.0

FBBase.py

FBBase.py

FBBase.py

FBBase.py

FBruntimePython

Python 2.5

FBruntimePython

Python 2.5

kinterbasdb 3.2.0

kinterbasdb 3.2.0

III - Python et les bases de données

III-A - L'API 2.0 de Python pour les bases de données (DBAPI)

III-A-1 - Introduction

Il n'est pas possible d'accéder en natif aux bases de données avec Python. Cependant l'ajout de bibliothèques conformes au standard DBAPI permet d'accéder à différentes bases de données.
On peut citer par exemple les modules kinterbasdb pour accéder aux bases Firebird/Interbase, mySQLdb pour les bases Mysql, psycopg pour les bases PostgreSQL …
Ce standard présenté par la suite permet d'accéder à différents types de bases de données sans grandes modifications de votre code. Une explication au chapitre V vous guidera si vous optez pour une autre BDD.

III-A-2 - La fonction connect

La fonction connect permet la connexion à une base de données. Le standard DBAPI recommande d'utiliser les paramètres suivants :
database : nom de la base de données à laquelle on veut se connecter ;
dsn : nom de la source de données utilisée pour la connexion ;
host : nom de la machine sur laquelle s'exécute la base de données ;
password : mot de passe pour la connexion ;
user : nom d'utilisateur pour la connexion.
La fonction connect renvoie un objet de la classe Connection.

Python est sensible à la casse. Les minuscules sont différentes des majuscules. Lorsque vous utiliserez des objets Python dans votre programme Delphi, il faudra également se souvenir que cette distinction est encore en vigueur.

import kinterbasdb
con = kinterbasdb.connect(host='localhost', database=r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb', \
user='sysdba', password='masterkey')

III-A-3 - Classe Connection

Quatre méthodes de la classe Connection vont nous intéresser :
close() : ferme la connexion à la base de données et libère les ressources ;
commit() : applique la transaction courante à la base de données ;
cursor() : renvoie une instance de la classe Cursor ;
rollback() : annule la transaction courante sur la base de données.

cur = con.cursor()

III-A-4 - Classe Cursor

Une instance de la classe Cursor représente l'ensemble des résultats d'une requête SQL.
Cette classe fournit de nombreuses méthodes. Nous ne présenterons que celles utilisées par la suite :
Close() : ferme le curseur et libère les ressources associées ;
execute(statement) : exécute une requête SQL statement sur la base de données ;
fetchall() : renvoie le résultat de la requête sous forme d'une liste de tuples.

Remarque: Un tuple est une séquence d'éléments immutables tandis qu'une liste est une séquence d'éléments mutables

cur.execute('select * from Country')
l = cur.fetchall()
print l
cur.close()

III-B - Le module FBBase.py - interface avec la BDD Firebird

III-B-1 - Introduction

Pour utiliser les modules fournis avec Python sur les bases de données, nous avions trois possibilités. La première possibilité serait d'utiliser les objets de la classe Connection et Cursor de façon brute. Mais en général, quelle que soit la base de données utilisée, on se crée une interface qui nous facilite l'utilisation de ces classes. Ici, cette interface peut se faire du côté de Delphi ou du côté de Python. J'ai opté pour la programmer en Python pour les raisons suivantes :
- il est plus simple de manipuler les objets Python avec Python qu'avec Delphi ;
- certains paramètres (notamment lors de la connexion à la base de données) entraînent une erreur de compilation sous Delphi, car termes non reconnus. Donc à moins de s'en sortir de manière peu propre, il était préférable de tout faire en Python et de gérer sous Delphi des objets Python que l'on maîtrise entièrement.

Néanmoins, l'interface proposée est une interface simple et minimaliste à l'exception de quelques fonctions. Elle pourra donc être complétée directement pour les besoins d'une base de données spécifique. Une autre possibilité est de se créer une nouvelle interface sous Delphi qui gérera la base de données en question en se liant à celle sous python qui fournit tous les outils unitaires à l'utilisation d'une base de données.

III-B-2 - L'objet TFBConnection

classe TFBConnection

import kinterbasdb
try:
if kinterbasdb.FB_API_VER == 20: kinterbasdb.init(type_conv=200)  ## Firebird 2.0.x
except: pass
import os.path
import os

class TFBConnection:
def __init__(self, host='localhost', pathbase=r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb',  \
user='sysdba', password='masterkey'):
self.pathbase=pathbase
self.host=host
self.user=user
self.password=password
def Initialize(self, host='localhost', pathbase=r'C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\employee.fdb',  \
user='sysdba', password='masterkey'):
self.pathbase=pathbase
self.host=host
self.user=user
self.password=password
def Connect(self): self.con = kinterbasdb.connect(host=self.host, database=self.pathbase, user=self.user, password=self.password)
def Commit(self): self.con.commit()
def Cursor(self): return TFBCursor(self)
def Rollback(self): self.con.rollback()
def Close(self): self.con.close()
## Fonctionnalite propre a Firebird
def DropDatabase(self): self.con.drop_database()
def CreateDatabase(self):
if self.host=='localhost':
try: os.makedirs(os.path.dirname(self.pathbase))
except: pass
else:
try: os.makedirs(os.path.dirname('\\\\'+self.host+self.pathbase))
except: pass
self.con = kinterbasdb.create_database( "create database '"+self.pathbase+"' user '"+self.user+"' password '"+ \
self.password+"'")

Cette classe est une interface à la classe Connection et à la fonction connect présentée précédemment. Elle permet de gérer une connexion à une BDD en reprenant les principales fonctions.

Tout d'abord, les trois premières lignes servent à l'importation de modules, kinterbasdb pour la gestion des bases Firebird, os et os.path pour la gestion des fichiers.

Présentation des méthodes de la classe TFBConnection

Méthodes de la classe TFBConnection

- __init__(self, host, database, user, password) : méthode appelée à la création de l'objet. Initialise les paramètres de la classe ;

- Connect(self) : lance la connexion à la BDD ;

- Commit(self) : applique la transaction courante à la base de données ;

- Cursor(self) : renvoie un objet de la classe TFBCursor ;

- Rollback(self) : annule la transaction courante sur la base de données ;

- Close(self) : ferme la connexion à la base de données et libère les ressources ;

- DropDatabase(self) : efface la base de données ;

- CreateDatabase(self): Crée la base de données. On crée si nécessaire le répertoire où sera sauvegardée la base.

Exemple d'utilisation de la classe TFBConnection

pyCon = TFBConnection()
pyCon.Connect()

III-B-3 - L'objet TFBCursor

classe TFBCursor

class TFBCursor(kinterbasdb.Cursor):
def __init__(self, FBConnection): kinterbasdb.Cursor.__init__(self, FBConnection.con)
def ExecuteElem(self, strreq): return (self.ExecuteListe(strreq))[0]
def ExecuteElem2(self, strreq): return (self.ExecuteTuple(strreq))[0]
def ExecuteTuple(self, strreq):
self.execute(strreq)
return self.fetchall()
def ExecuteIter(self, strreq):
self.execute(strreq)
return self.iter()
def ExecuteListe(self, strreq):
listresult=[]
for laliste in self.ExecuteTuple(strreq): listresult.append(laliste[0])
return listresult
## Fonctions clone de celles de base - permet d'avoir des references explicites dans ce code
def Close(self): self.close()
def Execute(self, strreq): self.execute(strreq)
def IterMap(self): return self.itermap()
def RowCount(self): return self.rowcount()
def FecthAll(self): return self.fetchall()
def FecthOne(self): return self.fetchone()
def Description(self): return self.description()
def CallProc(self, procname, params=()): return self.callproc(procname, params)
## Fonctionnalites supplementaires propres a Firebird
def ExecuteElemMap(self, strreq): return (self.ExecuteMap(strreq))[0]
def ExecuteMap(self, strreq):
self.execute(strreq)
return self.fetchallmap()
def ExecuteIterMap(self, strreq):
self.execute(strreq)
return self.itermap()
def ListeTable(self):
return self.ExecuteListe("select rdb$relation_name from rdb$relations where "+ \
"rdb$relation_name not like 'RDB$%';")
def ListeChamp(self, strtable):
l = self.ExecuteListe("Select RDB$FIELD_NAME from RDB$RELATION_FIELDS "+ \
"where RDB$RELATION_NAME = '"+strtable.upper()+"';")
for i in range(len(l)): l[i] = l[i].strip() ## La requete precedente laisse des espaces a la fin des noms
return l
def CreateGenerator(self, strgen, strtrig, strtable, strcolumn):
self.execute("CREATE GENERATOR "+strgen+";")
strSQL="CREATE TRIGGER "+strtrig+" FOR "+strtable+ \
" ACTIVE BEFORE INSERT POSITION 0 "+ \
"AS BEGIN "+ \
"IF( NEW."+strcolumn+" IS NULL ) THEN "+ \
"NEW."+strcolumn+" = GEN_ID("+strgen+", 1 );"+ \
"END"
self.execute(strSQL)

Cette classe est une interface à la classe Cursor présentée précédemment. Elle permet de manière simple d'exécuter des requêtes et de récupérer les résultats avec pour seule connaissance requise, le langage SQL.

Présentation de quelques méthodes de la classe TFBCursor

Méthodes de la classe TFBCursor

- __init__(self, FBConnection) : méthode appelée lors de la construction de l'objet ;

- Close(self) : ferme le curseur. Vous pouvez également utiliser close (sans le c majuscule) ;

- Execute(self, strreq) : exécute la requête SQL strreq. À utiliser sur des requêtes ne retournant aucun résultat tel que INSERT, UPDATE… Vous pouvez aussi utiliser la méthode execute (sans le e majuscule) ;

- ExecuteElem(self, strreq) : à utiliser pour récupérer le résultat unique d'une requête sur un seul champ ;

- ExecuteElem2(self, strreq) : à utiliser pour récupérer le tuple unique d'une requête sur un seul champ ;

- ExecuteTuple(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de tuples. À utiliser sur une requête SELECT ;

- ExecuteMap(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de map (ou dictionnaire). À utiliser sur une requête SELECT ;

- ExecuteListe(self, strreq) : exécute la requête SQL strreq et renvoie le résultat sous forme d'une liste de valeurs. À utiliser sur une requête SELECT qui ne contient qu'un seul champ de retour ;

- ListeTable(self) : retourne une liste des tables de la base de données. La requête utilisée n'est compatible qu'avec Firebird ;

- ListeChamp(self, strtable) : retourne une liste des champs de la table strtable de la base de données. La requête utilisée n'est compatible qu'avec Firebird ;

- CreateGenerator(self, strgen, strtrig, strtable, strcolumn) : permet d'appliquer à un champ une autoincrémentation. strtable et strcolumn correspondent à la table et au champ en question. strgen et strtrig correspondent au nom du générateur et du trigger.

Pour les requêtes du type SELECT, vous aurez le choix entre les méthodes ExecuteTuple et ExecuteMap. La première vous donnera un code plus concis, la deuxième un code plus clair.

Exemple d'utilisation de la classe TFBCursor

>>> pyCon = TFBConnection()
>>> pyCon.Connect()
>>> pyCur = pyCon.Cursor()
>>> lt = pyCur.ExecuteTuple('Select * from Country')
>>> lm = pyCur.ExecuteMap('Select * from Country')
>>> l = pyCur.ExecuteListe('Select CURRENCY from Country')
>>> print lt[5]
('Italy', 'Lira')
>>> print lm[5]
(result set row with COUNTRY = Italy, CURRENCY = Lira)
>>> print lm[5]['COUNTRY']
Italy
>>> print l[5]
Lira

IV - Programmation sous Delphi

Nous allons maintenant à partir de Delphi accéder à l'interface développée en Python.
Assurez-vous tout d'abord que vous avez installé Firebird et PythonForDelphi et que vous avez récupéré les sources du projet. Ces sources contiennent en plus le fichier FBBase.py et le runtime Python.
Ouvrez ensuite le projet AdminBase.dpr.

Si vous choisissez l'installation complète de Python, vous pouvez effacer le runtime (répertoire kinterbasdb et encodings et les fichiers .pyc et .pyd) ainsi que la dll Python25.dll (la suppression de la dll est obligatoire sinon l'application détectant cette dll ne cherchera le module kinterbasdb que dans ce répertoire (et ne le trouvera donc pas)).

IV-A - Connection à la base de données et initialisation des objets

Nous n'entrerons pas dans le détail de l'utilisation des composants fournis par PythonForDelphi, mais seulement pour ceux qui nous intéressent. Si vous souhaitez approfondir le sujet, vous trouverez des tutoriels sur le site de téléchargement de la bibliothèque et de nombreuses démos sont disponibles lors de l'installation des composants.

IV-A-1 - PythonEngine

Ce composant fait le lien avec la machine virtuelle Python. Dans votre programme Python, il ne peut y en avoir qu'un seul en même temps. C'est le seul composant de la barre de composants que nous utiliserons. Sur la fiche, vous voyez ce composant avec une tête de Python nommé PE (nom usuel).

IV-A-2 - Initialisation des objets

On remarquera dans les uses l'unité VarPyth (ajoutée à la « main »). Cette unité fournit des variables globales d'accès à des objets Python.

fonction FormCreate

procedure TfrmAdmin.FormCreate(Sender: TObject);
var
PyStrings:TStringList;
begin
Caption:='Visualisation d''une Base Firebird - Version '+Version ;
// Récupération du code du module python FBBase.py
PyStrings:=TStringList.Create;
PyStrings.LoadFromFile('FBBase.py');
// Exécution du code en mémoire dans le moteur Python
PE.ExecStrings(PyStrings);
// Création d'un objet TFBBase permettant de manipuler la base de données
pyCon:=MainModule.TFBConnection();
PyStrings.Free;
end;

Une première chose est de mettre en mémoire dans le moteur Python le code Python de l'interface FBBase.py. Pour cela la fonction PE.ExecStrings(pystr: Strings) permet d'exécuter le code source contenu dans pystr.
Tout objet défini dans pystr est alors directement accessible dans la variable globale MainModule.
MainModule.TFBConnection() appelle le constructeur __init__ de la classe TFBConnection et retourne une instance correspondante. Pour l'instant, notre instance pyCon est initialisée avec des valeurs par défaut (ceux définis dans la classe Python). Il s'agit donc des paramètres correspondant à la base de données que l'on a lors de l'installation de Firebird. Par contre, nous ne sommes pas encore connectés à cette base.
Tout objet Python est déclaré en Delphi en tant que variant.

IV-A-3 - Connexion à la base de données

fonction btnConnectClick

procedure TfrmAdmin.btnConnectClick(Sender: TObject);
var
pyltables,pylchamps:variant;
pyCur:variant;
i:integer;
begin
// connexion à la base de données avec les nouveaux paramètres
pyCon.host:=ehost.Text;
pyCon.database:=edatabase.Text;
pyCon.user:=euser.text;
pyCon.password:=epassword.Text;
try
pyCon.Connect() ;
...
end;

On accède aux attributs/fonctions d'une instance Python de la même façon qu'en Delphi.
La première chose est de se connecter à la base avec pyCon.Connect().

En Python, le typage est dynamique; on peut donc associer à un attribut n'importe quel type et le changer à tout moment.

Encore une fois, il faut bien respecter la casse sur l'utilisation des attributs/fonctions de pyCon.

IV-B - Utilisation de requêtes SQL

IV-B-1 - Exemple 1

fonction btnConnectClick

procedure TfrmAdmin.btnConnectClick(Sender: TObject);
var
pyltables,pylchamps:variant;
pyCur:variant;
i:integer;
begin
...
// Création d'un curseur
pyCur:=pyCon.Cursor();
// On liste les tables de la base et on remplit un comboBox
pyltables := pyCur.ListeTable();
comboTable.Clear;
for i:=0 to len(pyltables)-1 do
comboTable.items.Add(pyltables.GetItem(i));
comboTable.ItemIndex:=0;
// On liste les champs de la table sélectionnée et on remplit un combobox
pylchamps:= pyCur.ListeChamps(comboTable.Text);
comboChamp.Clear;
for i:=0 to len(pylchamps)-1 do
comboChamp.items.Add(pylchamps.GetItem(i));
combochamp.ItemIndex:=0;
btnAfficherTuple.Enabled:=true;
btnAfficherMap.Enabled:=true;
pyCur.Close()
except
MessageDlg('Erreur : La connexion a la base de données n''a pu se faire', mtError, [mbOk], 0);
end;
end;

procedure TfrmAdmin.comboTableChange(Sender: TObject);
var
pylchamps:variant;
i:integer;
pyCur:variant;
begin
pyCur:=pyCon.Cursor();
// Si on sélectionne une nouvelle table, on met à jour la comboBox des champs.
pylchamps:= pyCur.ListeChamp(comboTable.Text);
comboChamp.Clear;
for i:=0 to len(pylchamps)-1 do
comboChamp.items.Add(pylchamps.GetItem(i));
combochamp.ItemIndex:=0;
pyCur.Close()
end;

Voici le premier exemple de l'utilisation d'un objet TFBCursor.
Lorsque vous cliquez sur le bouton « Connecter », le programme remplit les combobox de l'encadré Requête avec le nom des tables de la base de données et le nom des champs de la table sélectionnée. Pour cela, on utilise le curseur pyCur et les fonctions vues auparavant ListeTable et ListeChamp.
Tout résultat retourné par une fonction Python sera récupéré dans une variable Delphi de type variant. Lorsque vous changez la table, la deuxième combobox est immédiatement modifiée pour recevoir les champs correspondants.

Cependant, Delphi ne permet pas d'utiliser la propriété d'index sur un variant si celui-ci n'est pas un vrai tableau de variant, c'est-à-dire qu'il ne sera pas possible d'accéder directement à pyltables[0]. En effet, notre variable représente une liste de string. Ce problème est contourné par l'unité VarPyth qui détecte des noms spéciaux accolés à une variable variant: GetItem, SetItem, Length… Ainsi on accédera à un élément avec la syntaxe pyltables.GetItem(0).

On notera également la fonction global len (équivalent à .Length()) qui permet de connaître la longueur d'une liste.

Je vous encourage à parcourir le fichier VarPyth.pas pour de plus amples explications.

IV-B-2 - Exemple 2

Pour afficher le résultat de la requête, nous disposons de deux boutons. Le premier utilisera la fonction ExecuteTuple et le deuxième ExecuteMap. Le résultat sera cependant identique.

Fonction btnAfficherTupleClick

procedure TfrmAdmin.btnAfficherTupleClick(Sender: TObject);
var
pyValue,pyChamps:variant;
pyCur:variant;
i,j:integer;
begin
pyCur:=pyCon.Cursor();
// On récupère le nom des champs de la table et on l'affiche dans la StringGrid
pyChamps:=pyCur.ListeChamps(comboTable.Text);
sgTable.ColCount:=len(pyChamps);
for j:=0 to len(pyChamps)-1 do
begin
sgTable.Cells[j,0]:=pyChamps.GetItem(j);
sgTable.ColWidths[j]:=100;
end;
// On récupère toutes les valeurs de la table sélectionnée avec tri selon le champ sélectionné
pyValue:=pyCur.ExecuteTuple('select * from '+comboTable.Text+' order by '+combochamp.Text);
sgTable.RowCount:=len(pyValue)+1;
for i:=0 to len(pyValue)-1 do
for j:=0 to len(pyValue.GetItem(i))-1 do
sgTable.Cells[j,i+1]:=pyValue.GetItem(i).GetItem(j);
pyCur.Close()
end;

Dans ce deuxième exemple, on va afficher une table complète. Pour cela on exécute la requête pyCur.ExecuteTuple('select * from '+comboTable.Text+' order by '+combochamp.Text); qui nous retourne une liste de tuples (équivalent à une liste de listes). Pour accéder à un élément, il suffira de doubler l'utilisation du GetItem : pyValue.GetItem(i).GetItem(j)

procedure TfrmAdmin.btnAfficherMapClick(Sender: TObject);
var
pyValue,pyChamps:variant;
pyCur:variant;
i,j:integer;
begin
pyCur:=pyCon.Cursor();
// On récupère le nom des champs de la table et on l'affiche dans la StringGrid
pyChamps:=pyCur.ListeChamp(comboTable.Text);
sgTable.ColCount:=len(pyChamps);
for j:=0 to len(pyChamps)-1 do
begin
sgTable.Cells[j,0]:=pyChamps.GetItem(j);
sgTable.ColWidths[j]:=100;
end;
// On récupère toutes les valeurs de la table sélectionnée avec tri selon le champ sélectionné
pyValue:=pyCur.ExecuteMap('select * from '+comboTable.Text+' order by '+combochamp.Text);
sgTable.RowCount:=len(pyValue)+1;
for i:=0 to len(pyValue)-1 do
for j:=0 to len(pyValue.GetItem(i))-1 do
sgTable.Cells[j,i+1]:=pyValue.GetItem(i).GetItem(pyChamps.GetItem(j));
pyCur.Close()
end;

Cette fonction est identique à la précédente, excepté les lignes pyValue:=pyCur.ExecuteMap(…) et sgTable.Cells[j,i+1]:=pyValue.GetItem(i).GetItem(pyChamps.GetItem(j));. Pour accéder à un élément, on n'utilise plus un index numérique, mais le nom d'un champ (pour conserver la généricité de la fonction, le nom du champ se trouve donc dans pyChamps.GetItem(j)).

V - Pour aller plus loin

Ce tutoriel et les sources fournies ont été conçus pour fonctionner avec les bases Firebird. Cependant on a vu que d'autres bibliothèques Python pour l'accès à d'autres bases de données respectent le standard DBAPI. Il est donc envisageable d'utiliser la même interface Python pour d'autres bases de données.

Je propose donc une nouvelle interface pyBase.py qui fonctionnera avec les bases de données suivantes :
Firebird, MySQL

V-A - Interface pyBase.py

Vous pouvez télécharger cette interface ici. Le nom des classes a un peu changé afin d'avoir une nomenclature générale. Un paramètre supplémentaire name a été ajouté pour la construction de l'instance TpyConnection qui vous permettra de sélectionner le type de la base de données utilisée.

V-B - Déploiement de vos applications

Outre votre exécutable créé sous Delphi et les fichiers dépendants, il vous faudra comme précédemment copier ou installer d'autres fichiers.

V-B-1 - Interface pyBase.py

Il vous suffit de copier ce fichier à la racine du répertoire de votre application

V-B-2 - Dépendances Python

Cette fois-ci, je n'ai pas préparé de runtime. Vous ne serez pas obligé d'installer toutes les librairies, mais juste celles correspondantes aux bases de données souhaitées. Vous trouverez tous les liens nécessaires ici. À l'heure actuelle, il n'existe pas d'installateur pour MySQLdb compatible Python 2.5 n'étant pas disponible, vous devrez vous rabattre sur des versions compatibles 2.4 (y compris pour toutes les autres bibliothèques) :

- Python 2.4.4 (pour toutes les BDD) ;

- kinterbasdb 3.2.0 (Firebird) ;

- MySQLdb 1.2.1 (MySQL).

V-B-3 - Ajout de nouvelles bases de données

Si vous voulez utiliser d'autres BDD, il vous faudra tout d'abord modifier le fichier pyBase.py :
- en premier lieu, ajout de l'importation du module correspondant au début du fichier ;
- modification de la fonction Connect de la classe TpyConnection.

Pour le déploiement, comme la construction du runtime est assez compliquée (j'y travaille pour essayer de le rendre plus simple), je vous conseille d'opter pour l'installation complète.
Voici une liste des modules à récupérer si vous souhaitez tester d'autres bases de données. Je n'ai pas testé ces modules, mais vous devriez trouver sur les sites la documentation nécessaire.

BDD

Module

Lien

Firebird/Interbase

kinterbasdb

http://kinterbasdb.sourceforge.net/

MySQL

MySQLdb

http://sourceforge.net/projects/mysql-python

ODBC

mxODBC

http://www.egenix.com/

Oracle

DCOracle2

http://www.computronix.com/utilities.shtml

MSSQL Server

mssqldb

http://www.object-craft.com.au/projects/mssql/

DB2

DB2

ftp://people.linuxkorea.co.kr/pub/DB2/ [ftp://people.linuxkorea.co.kr/pub/DB2/]

PostGreSQL

psycopg

http://initd.org/projects/psycopg1

SAP DB

sapdbapi

http://www.sapdb.org/sapdbapi.html

Sybase

Sybase

http://www.object-craft.com.au/projects/sybase/

VI - Téléchargement

Voici le récapitulatif de ce que vous pouvez télécharger :

Fichier

Description

Taille

Mode FTP

Mode HTTP de secours

FBBase.py

Interface Python/Firebird

7 Ko

FBBase.py

FBBase.py

pyBase.py

Interface Python/Firebird/MySQL

7 Ko

pyBase.py

pyBase.py

FBAdminBase0.2.1.zip

Source + exécutable de l'application sans les dépendances Python

307 Ko

FBAdminBase0.2.1.zip

FBAdminBase0.2.1.zip

FBRuntimePython.zip

Runtime Python/Firebird

1.63 Mo

FBRuntimePython.zip

FBRuntimePython.zip

FBAdminBase0.2.1-FBRuntimePython-FBBase.py.zip

Application complète + Runtime Python/Firebird + FBBase

1.94 Mo

FBAdminBase0.2.1-FBRuntimePython-FBBase.py.zip

FBAdminBase0.2.1-FBRuntimePython-FBBase.py.zip

VII - Conclusion

L'accès aux bases de données sous une version personnelle de Delphi se fait de manière très simple grâce à l'utilisation des composants PythonForDelphi. De plus aucune connaissance en Python n'est nécessaire, d'où l'intérêt pour les Delphiistes.