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

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

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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é. 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.
Ecrit en juillet 2005, ce tutoriel a été revue 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:

Image non disponible

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:

Image non disponible

Arrivé à cette fenêtre,

Image non disponible



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 pouvoir compiler vos programmes Delphi avec différentes version 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,

Image non disponible


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.

Image non disponible

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

Image non disponible

Et sous Delphi 2005:

Image non disponible

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 2 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 auto extractibles 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) Deploiement (version Runtime) Deploiement (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.

 
Sélectionnez

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

4 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

 
Sélectionnez

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

 
Sélectionnez

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 3 possibilités. La première possibilité serait d'utiliser les objets de la classe Connection et Cursor de façon brut. Mais en général, quelque 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
Sélectionnez

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 3 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 ensuite les 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éé si nécessaire le répertoire où sera sauvegardée la base.
Exemple d'utilisation de la classe TFBConnection
Sélectionnez

pyCon = TFBConnection()
pyCon.Connect()

III-B-3. L'objet TFBCursor

classe TFBCursor
Sélectionnez

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 requis, 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. A 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): A utiliser pour récupérer le résultat unique d'une requête sur un seul champ.
  • ExecuteElem2(self, strreq): A 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. A 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). A 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. A 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 auto-incré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ête 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
Sélectionnez

>>> 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 fournit par PythonForDelphi mais seulement pour ceux qui nous intéressent. Si vous souhaitez approfondir le sujet, vous trouverez des tutoriaux 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
Sélectionnez

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
Sélectionnez

procedure TfrmAdmin.btnConnectClick(Sender: TObject);
var
  pyltables,pylchamps:variant;
  pyCur:variant;
  i:integer;
begin
    // connection à 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
Sélectionnez

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 cliquer 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 changer 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 2 bouttons. Le premier utilisera la fonction ExecuteTuple et le deuxième ExecuteMap. Le résultat sera cependant identique.

Fonction btnAfficherTupleClick
Sélectionnez

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 champs 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)

 
Sélectionnez

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 champs 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ée 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 de sélectionner le type de la base de données utilisées.

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 simplement 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. A 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 BDDs)
  • kinterbasdb 3.2.0 (Firebird)
  • MySQLdb 1.2.1 (MySQL)

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

Si vous voulez utilisez d'autres BDDs, 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é (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.

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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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.