Les bases – Real Python

By | décembre 2, 2020

Formation gratuite Python

La création d'applications utilisant une base de données SQL est une tâche de programmation assez courante. Les bases de données SQL sont partout et ont un excellent support en Python. Dans la programmation GUI, PyQt fournit un support de base de données SQL robuste et multiplateforme qui vous permet de créer, de vous connecter et de gérer vos bases de données de manière cohérente.

La prise en charge SQL de PyQt s’intègre entièrement à son architecture Model-View pour vous aider dans le processus de création d’applications de base de données.

Les exemples de ce didacticiel nécessitent une connaissance de base du langage SQL, en particulier du système de gestion de base de données SQLite. Une connaissance préalable de la programmation GUI avec Python et PyQt sera également utile.

Connexion de PyQt à une base de données SQL

La connexion d'une application à une base de données relationnelle et l'obtention de l'application pour créer, lire, mettre à jour et supprimer les données stockées dans cette base de données est une tâche courante en programmation. Les bases de données relationnelles sont généralement organisées en un ensemble de les tables, ou rapports. Une ligne donnée dans un tableau est appelée record ou tuple, et une colonne est appelée attribut.

Chaque colonne stocke un type spécifique d'informations, telles que des noms, des dates ou des nombres. Chaque ligne représente un ensemble de données étroitement liées et chaque ligne a la même structure générale. Par exemple, dans une base de données qui stocke des données sur les employés d'une entreprise, une ligne spécifique représente un employé individuel.

La plupart des systèmes de bases de données relationnelles utilisent SQL (langage de requête structuré) pour interroger, manipuler et gérer les données contenues dans la base de données. SQL est un langage de programmation déclaratif et spécifique à un domaine spécialement conçu pour communiquer avec des bases de données.

Les systèmes de bases de données relationnelles et SQL sont largement utilisés de nos jours. Vous trouverez plusieurs systèmes de gestion de bases de données différents, tels que SQLite, PostgreSQL, MySQL, MariaDB et bien d’autres. Vous pouvez connecter Python à l'un de ces systèmes de base de données à l'aide d'une bibliothèque SQL Python dédiée.

En ce qui concerne la programmation d'interface graphique avec Python et PyQt, PyQt fournit un ensemble robuste de classes pour travailler avec des bases de données SQL. Cet ensemble de classes sera votre meilleur allié lorsque vous aurez besoin de connecter votre application à une base de données SQL.

Dans ce didacticiel, vous apprendrez les bases de l’utilisation de la prise en charge SQL de PyQt pour créer des applications GUI qui interagissent de manière fiable avec des bases de données relationnelles pour lire, écrire, supprimer et afficher des données.

Création d'une connexion à la base de données

La connexion de vos applications à une base de données SQL physique est une étape importante dans le processus de développement d'applications de base de données avec PyQt. Pour réussir cette étape, vous avez besoin d'informations générales sur la configuration de votre base de données.

Par exemple, vous devez savoir sur quel système de gestion de base de données votre base de données est construite, et vous devrez peut-être également avoir un nom d'utilisateur, un mot de passe, un nom d'hôte, etc.

Dans ce didacticiel, vous utiliserez SQLite 3, un système de base de données bien testé avec une prise en charge sur toutes les plates-formes et des exigences de configuration minimales. SQLite vous permet de lire et d'écrire directement dans les bases de données de votre disque local sans avoir besoin d'un processus serveur distinct. Cela en fait une option conviviale pour l'apprentissage du développement d'applications de base de données.

Un autre avantage de l'utilisation de SQLite est que la bibliothèque est livrée avec Python et aussi avec PyQt, vous n'avez donc pas besoin d'installer quoi que ce soit d'autre pour commencer à travailler avec.

Dans PyQt, vous pouvez créer une connexion à la base de données en utilisant le QSqlDatabase classe. Cette classe représente une connexion et fournit une interface pour accéder à la base de données. Pour créer une connexion, il suffit d'appeler .addDatabase () sur QSqlDatabase. Cette méthode statique prend un pilote SQL et un nom de connexion facultatif comme arguments et renvoie une connexion à la base de données:

QSqlDatabase.addDatabase(
    chauffeur, connectionName=QSqlDatabase.defaultConnection
)

Le premier argument, chauffeur, est un argument obligatoire contenant une chaîne contenant le nom d'un pilote SQL pris en charge par PyQt. Le deuxième argument, connectionName, est un argument facultatif contenant une chaîne avec le nom de la connexion. connectionName par défaut QSqlDatabase.defaultConnection, qui contient normalement la chaîne "qt_sql_default_connection".

Si vous avez déjà une connexion appelée connectionName, puis cette connexion est supprimée et remplacée par une nouvelle connexion, et .addDatabase () renvoie la connexion à la base de données nouvellement ajoutée à l'appelant.

Un appel à .addDatabase () ajoute une connexion de base de données à une liste de connexions disponibles. Cette liste est un registre mondial que PyQt maintient dans les coulisses pour garder une trace des connexions disponibles dans une application. Enregistrer vos connexions avec un connectionName vous permettra de gérer plusieurs connexions dans une application de base de données.

Une fois que vous avez créé une connexion, vous devrez peut-être définir plusieurs attributs dessus. L'ensemble spécifique d'attributs dépendra du pilote que vous utilisez. En général, vous devrez définir des attributs tels que le nom de la base de données, le nom d'utilisateur et le mot de passe pour accéder à la base de données.

Voici un résumé des méthodes de définition que vous pouvez utiliser pour définir les attributs ou propriétés les plus couramment utilisés d'une connexion à une base de données:

Méthode La description
.setDatabaseName (nom) Définit le nom de la base de données sur Nom, qui est une chaîne représentant un nom de base de données valide
.setHostName (hôte) Définit le nom d'hôte sur hôte, qui est une chaîne représentant un nom d'hôte valide
.setUserName (nom d'utilisateur) Définit le nom d'utilisateur sur Nom d'utilisateur, qui est une chaîne représentant un nom d'utilisateur valide
.setPassword (mot de passe) Définit le mot de passe sur mot de passe, qui est une chaîne représentant un mot de passe valide

Notez que le mot de passe que vous passez en argument .setPassword () est stocké en texte brut et peut être récupéré plus tard en appelant .mot de passe(). C'est un sérieux risque de sécurité que vous devez éviter d'introduire dans vos applications de base de données. Vous apprendrez une approche plus sûre dans la section Ouverture d'une connexion à une base de données plus loin dans ce didacticiel.

Pour créer une connexion à une base de données SQLite à l'aide de QSqlDatabase, ouvrez une session interactive Python et saisissez le code suivant:

>>>

>>> de PyQt5.QtSql importer QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> con


>>> con.nom de la base de données()
'contacts.sqlite'

>>> con.connectionName()
'qt_sql_default_connection'

Ce code créera un objet de connexion à la base de données en utilisant "QSQLITE" comme pilote de la connexion et "contacts.sqlite" comme nom de base de données de la connexion. Puisque vous ne transmettez pas de nom de connexion à .addDatabase (), la connexion nouvellement créée devient votre connexion par défaut, dont le nom est "qt_sql_default_connection".

Dans le cas des bases de données SQLite, le nom de la base de données est normalement un nom de fichier ou un chemin qui inclut le nom de fichier de la base de données. Vous pouvez également utiliser le nom spécial ":Mémoire:" pour une base de données en mémoire.

Gestion de plusieurs connexions

Il peut y avoir des situations dans lesquelles vous devez utiliser plusieurs connexions à une seule base de données. Par exemple, vous souhaiterez peut-être enregistrer les interactions des utilisateurs avec la base de données en utilisant une connexion spécifique pour chaque utilisateur.

Dans d'autres situations, vous devrez peut-être connecter votre application à plusieurs bases de données. Par exemple, vous souhaiterez peut-être vous connecter à plusieurs bases de données distantes afin de collecter des données pour alimenter ou mettre à jour une base de données locale.

Pour gérer ces situations, vous pouvez fournir des noms spécifiques pour vos différentes connexions et référencer chaque connexion par son nom. Si vous voulez donner un nom à votre connexion à la base de données, transmettez ce nom comme deuxième argument à .addDatabase ():

>>>

>>> de PyQt5.QtSql importer QSqlDatabase

>>> # Première connexion
>>> con1 = QSqlDatabase.addDatabase("QSQLITE", "con1")
>>> con1.setDatabaseName("contacts.sqlite")

>>> # Deuxième connexion
>>> con2 = QSqlDatabase.addDatabase("QSQLITE", "con2")
>>> con2.setDatabaseName("contacts.sqlite")

>>> con1

>>> con2


>>> con1.nom de la base de données()
'contacts.sqlite'
>>> con2.nom de la base de données()
'contacts.sqlite'

>>> con1.connectionName()
'con1'
>>> con2.connectionName()
'con2'

Ici, vous créez deux connexions différentes à la même base de données, contacts.sqlite. Chaque connexion a son propre nom de connexion. Vous pouvez utiliser le nom de la connexion pour obtenir une référence à une connexion spécifique à tout moment ultérieurement dans votre code en fonction de vos besoins. Pour ce faire, vous pouvez appeler .base de données() avec un nom de connexion:

>>>

>>> de PyQt5.QtSql importer QSqlDatabase

>>> db = QSqlDatabase.base de données("con1", ouvert=Faux)

>>> db.nom de la base de données()
'contacts.sqlite'
>>> db.connectionName()
'con1'

Dans cet exemple, vous voyez que .base de données() prend deux arguments:

  1. connectionName contient le nom de connexion que vous devez utiliser. Si vous ne transmettez pas de nom de connexion, la connexion par défaut sera utilisée.
  2. ouvert contient une valeur booléenne qui indique .base de données() si vous souhaitez ouvrir automatiquement la connexion ou non. Si ouvert est Vrai (valeur par défaut) et que la connexion n'est pas ouverte, la connexion est ouverte automatiquement.

La valeur de retour de .base de données() est une référence à l'objet de connexion appelé connectionName. Vous pouvez utiliser différents noms de connexion pour obtenir des références à des objets de connexion spécifiques, puis les utiliser pour gérer votre base de données.

Utilisation de différents SQL Divers

Jusqu'à présent, vous avez appris à créer une connexion à une base de données à l'aide de Pilote SQLite. Ce n’est pas le seul pilote disponible dans PyQt. La bibliothèque fournit un ensemble complet de pilotes SQL qui vous permettent d'utiliser différents types de systèmes de gestion de base de données en fonction de vos besoins spécifiques:

Nom du conducteur Système de gestion de base de données
QDB2 IBM Db2 (version 7.1 et supérieure)
QIBASE Borland InterBase
QMYSQL / MARIADB MySQL ou MariaDB (version 5.0 et supérieure)
QOCI Interface d'appel d'Oracle
QODBC Connectivité de base de données ouverte (ODBC)
QPSQL PostgreSQL (versions 7.3 et supérieures)
QSQLITE2 SQLite 2 (obsolète depuis Qt 5.14)
QSQLITE SQLite 3
QTDS Sybase Adaptive Server (obsolète depuis Qt 4.7)

La colonne Nom du pilote contient le chaînes d'identifiant que vous devez passer à .addDatabase () comme premier argument pour utiliser le pilote associé. Contrairement au pilote SQLite, lorsque vous utilisez un pilote différent, vous devrez peut-être définir plusieurs attributs, tels que nom de la base de données, hostName , Nom d'utilisateur, et mot de passe, pour que la connexion fonctionne correctement.

Les pilotes de base de données sont dérivés de QSqlDriver. Vous pouvez créer vos propres pilotes de base de données en sous-classant QSqlDriver, mais ce sujet dépasse le cadre de ce didacticiel. Si vous souhaitez créer vos propres pilotes de base de données, consultez Comment écrire votre propre pilote de base de données pour plus de détails.

Ouverture d'une connexion à une base de données

Une fois que vous avez une connexion à la base de données, vous devez ouvrir cette connexion pour pouvoir interagir avec votre base de données. Pour faire ça, tu appelles .ouvert() sur l'objet de connexion. .ouvert() a les deux variantes suivantes:

  1. .ouvert() ouvre une connexion à la base de données en utilisant les valeurs de connexion actuelles.
  2. .open (nom d'utilisateur, mot de passe) ouvre une connexion à la base de données à l'aide du Nom d'utilisateur et mot de passe.

Les deux variantes reviennent Vrai si la connexion réussit. Sinon, ils reviennent Faux. Si la connexion ne peut pas être établie, vous pouvez appeler .lastError () pour obtenir des informations sur ce qui s'est passé. Cette fonction renvoie des informations sur la dernière erreur signalée par la base de données.

Voici un exemple d’ouverture d’une connexion à une base de données SQLite à l’aide de la première variante de .ouvert():

>>>

>>> de PyQt5.QtSql importer QSqlDatabase

>>> # Créer la connexion
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Ouvrez la connexion
>>> con.ouvert()
Vrai
>>> con.est ouvert()
Vrai

Dans l'exemple ci-dessus, vous créez d'abord une connexion à votre base de données SQLite et ouvrez cette connexion à l'aide de .ouvert(). Depuis .ouvert() Retour Vrai, la connexion est réussie. À ce stade, vous pouvez vérifier la connexion en utilisant .est ouvert(), qui renvoie Vrai si la connexion est ouverte et Faux autrement.

Dans les applications du monde réel, vous devez vous assurer que vous disposez d'une connexion valide à votre base de données avant d'essayer d'effectuer des opérations sur vos données. Sinon, votre application peut tomber en panne et échouer. Par exemple, que se passe-t-il si vous ne disposez pas des autorisations d'écriture pour le répertoire dans lequel vous essayez de créer ce fichier de base de données? Vous devez vous assurer que vous gérez toute erreur pouvant survenir lors de l'ouverture d'une connexion.

Une façon courante d'appeler .ouvert() est de l'envelopper dans une instruction conditionnelle. Cela vous permet de gérer les erreurs qui peuvent survenir lors de l'ouverture de la connexion:

>>>

>>> importer sys
>>> de PyQt5.QtSql importer QSqlDatabase

>>> # Créer la connexion
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Ouvrez la connexion et gérez les erreurs
>>> si ne pas con.ouvert():
...     impression("Impossible de se connecter à la base de données")
...     sys.sortie(1)

Conclure l'appel à .ouvert() dans une instruction conditionnelle vous permet de gérer toute erreur qui se produit lorsque vous ouvrez la connexion. De cette façon, vous pouvez informer vos utilisateurs de tout problème avant l'exécution de l'application. Notez que l'application se termine avec un statut de sortie de 1, qui est habituellement utilisé pour indiquer un échec du programme.

Dans l'exemple ci-dessus, vous utilisez .ouvert() dans une session interactive, donc vous utilisez impression() pour présenter des messages d'erreur aux utilisateurs. Cependant, dans les applications GUI, au lieu d'utiliser impression(), vous utilisez normalement un QMessageBox objet. Avec QMessageBox, vous pouvez créer de petites boîtes de dialogue pour présenter des informations à vos utilisateurs.

Voici un exemple d’application GUI qui illustre un moyen de gérer les erreurs de connexion:

    1importer sys
    2
    3de PyQt5.QtSql importer QSqlDatabase
    4de PyQt5.QtWidgets importer QApplication, QMessageBox, QLabel
    5
    6# Créer la connexion
    septcon = QSqlDatabase.addDatabase("QSQLITE")
    8con.setDatabaseName("/home/contacts.sqlite")
    9
dix# Créer l'application
11app = QApplication(sys.argv)
12
13# Essayez d'ouvrir la connexion et de gérer les erreurs possibles
14si ne pas con.ouvert():
15    QMessageBox.critique(
16        Aucun,
17        "Nom de l'application - Erreur!",
18        "Erreur de la base de données: % s" % con.lastError().databaseText(),
19    )
20    sys.sortie(1)
21
22# Créer la fenêtre de l'application
23gagner = QLabel("Connexion ouverte avec succès!")
24gagner.setWindowTitle("Nom de l'application")
25gagner.redimensionner(200, 100)
26gagner.montrer()
27sys.sortie(app.exec_())

le si l'instruction à la ligne 14 vérifie si la connexion a échoué. Si la /domicile/ le répertoire n’existe pas ou si vous n’avez pas la permission d’y écrire, alors l’appel à .ouvert() échoue car le fichier de base de données ne peut pas être créé. Dans cette situation, le flux d'exécution entre le si bloc de code d'instruction et affiche un message à l'écran.

Si vous modifiez le chemin vers un autre répertoire dans lequel vous pouvez écrire, l'appel à .ouvert() réussira et vous verrez une fenêtre affichant le message Connexion ouverte avec succès! Vous aurez également un nouveau fichier de base de données appelé contacts.sqlite dans le répertoire sélectionné.

Notez que vous passez Aucun comme le message parent car, au moment de l’affichage du message, vous n’avez pas encore créé de fenêtre. Vous n’avez donc pas de parent viable pour la boîte de message.

Exécution de requêtes SQL avec PyQt

Avec une connexion de base de données entièrement fonctionnelle, vous êtes prêt à commencer à travailler avec votre base de données. Pour ce faire, vous pouvez utiliser des requêtes SQL basées sur des chaînes et QSqlQuery objets. QSqlQuery vous permet d'exécuter n'importe quel type de requête SQL dans votre base de données. Avec QSqlQuery, vous pouvez exécuter des instructions de langage de manipulation de données (DML), telles que SÉLECTIONNER, INSÉRER, MISE À JOUR, et SUPPRIMER, ainsi que des instructions DDL (Data Definition Language), telles que CRÉER UNE TABLE etc.

Le constructeur de QSqlQuery propose plusieurs variantes, mais dans ce didacticiel, vous en découvrirez deux:

  1. QSqlQuery (requête, connexion) construit un objet de requête à l'aide d'un SQL basé sur une chaîne requete et une base de données connexion. Si vous ne spécifiez pas de connexion ou si la connexion spécifiée n'est pas valide, la connexion à la base de données par défaut est utilisée. Si requete n'est pas une chaîne vide, elle sera exécutée immédiatement.

  2. QSqlQuery (connexion) construit un objet de requête en utilisant connexion. Si connexion n'est pas valide, la connexion par défaut est utilisée.

Vous pouvez également créer QSqlQuery objets sans passer d'arguments au constructeur. Dans ce cas, la requête utilisera la connexion à la base de données par défaut, le cas échéant.

Pour exécuter une requête, vous devez appeler .exec () sur l'objet de requête. Vous pouvez utiliser .exec () de deux manières différentes:

  1. .exec (requête) exécute la requête SQL basée sur une chaîne contenue dans requete. Il retourne Vrai si la requête a réussi et retourne autrement Faux.

  2. .exec () exécute une requête SQL préalablement préparée. Il retourne Vrai si la requête a réussi et retourne autrement Faux.

Maintenant que vous connaissez les bases de l'utilisation QSqlQuery pour créer et exécuter des requêtes SQL, vous êtes prêt à apprendre à mettre vos connaissances en pratique.

Exécution de requêtes SQL statiques

Pour commencer à créer et exécuter des requêtes avec PyQt, vous allez lancer votre éditeur de code ou IDE préféré et créer un script Python appelé queries.py. Enregistrez le script et ajoutez-y le code suivant:

    1importer sys
    2
    3de PyQt5.QtSql importer QSqlDatabase, QSqlQuery
    4
    5# Créer la connexion
    6con = QSqlDatabase.addDatabase("QSQLITE")
    septcon.setDatabaseName("contacts.sqlite")
    8
    9# Ouvrez la connexion
dixsi ne pas con.ouvert():
11    impression("Erreur de la base de données: % s" % con.lastError().databaseText())
12    sys.sortie(1)
13
14# Créez une requête et exécutez-la tout de suite en utilisant .exec ()
15createTableQuery = QSqlQuery()
16createTableQuery.exec(
17    "" "
18                CREATE TABLE contacts (
19                                id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NON NULL,
20                                nom VARCHAR (40) NOT NULL,
21                                emploi VARCHAR (50),
22                                email VARCHAR (40) NOT NULL
23                )
24                "" "
25)
26
27impression(con.les tables())

Dans ce script, vous commencez par importer les modules et les classes avec lesquels vous allez travailler. Ensuite, vous créez une connexion à la base de données en utilisant .addDatabase () avec le pilote SQLite. Vous définissez le nom de la base de données sur "contacts.sqlite" et ouvrez la connexion.

Pour créer votre première requête, vous instanciez QSqlQuery sans aucun argument. Avec l'objet de requête en place, vous appelez .exec (), en passant une requête SQL basée sur une chaîne comme argument. Ce type de requête est appelé requête statique car il n'obtient aucun paramètre extérieur à la requête.

La requête SQL ci-dessus crée une nouvelle table appelée Contacts dans votre base de données. Ce tableau comportera les quatre colonnes suivantes:

Colonne Contenu
id Un entier avec la clé primaire de la table
Nom Une chaîne avec le nom d'un contact
emploi Une chaîne avec l'intitulé du poste d'un contact
email Une chaîne avec l'email d'un contact

La dernière ligne du script ci-dessus imprime la liste des tables contenues dans votre base de données. Si vous exécutez le script, vous noterez qu’un nouveau fichier de base de données appelé contacts.sqlite est créé dans votre répertoire actuel. Vous obtiendrez également quelque chose comme ['contacts', 'sqlite_sequence'] imprimé sur votre écran. Cette liste contient les noms des tables de votre base de données.

Appel .exec () sur un QSqlQuery object est un moyen courant d'exécuter immédiatement des requêtes SQL basées sur des chaînes sur vos bases de données, mais que se passe-t-il si vous souhaitez préparer vos requêtes à l'avance pour une exécution ultérieure? C'est le sujet de la section suivante.

Exécution de requêtes dynamiques: formatage de chaîne

Jusqu'à présent, vous avez appris à exécuter des requêtes statiques sur une base de données. Les requêtes statiques sont celles qui n'acceptent pas paramètres, donc la requête s'exécute telle quelle. Même si ces requêtes sont assez utiles, vous devez parfois créer des requêtes qui récupèrent des données en réponse à certains paramètres d'entrée.

Les requêtes qui acceptent des paramètres au moment de l'exécution sont appelées requêtes dynamiques. L'utilisation de paramètres vous permet d'affiner la requête et de récupérer des données en réponse à des valeurs de paramètres spécifiques. Des valeurs différentes produiront des résultats différents. Vous pouvez prendre des paramètres d'entrée dans une requête en utilisant l'une des deux approches suivantes:

  1. Créez la requête de manière dynamique, en utilisant la mise en forme de chaîne pour interpoler les valeurs des paramètres.
  2. Préparez la requête à l'aide de paramètres d'espace réservé, puis liez des valeurs spécifiques à des paramètres.

La première approche vous permet de créer rapidement des requêtes dynamiques. Cependant, pour utiliser cette approche en toute sécurité, vous devez vous assurer que les valeurs de vos paramètres proviennent d'une source fiable. Sinon, vous risquez de faire face à des attaques par injection SQL.

Voici un exemple d'utilisation de la mise en forme de chaîne pour créer des requêtes dynamiques dans PyQt:

>>>

>>> de PyQt5.QtSql importer QSqlQuery, QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")
>>> con.ouvert()
Vrai

>>> Nom = «Linda»
>>> emploi = "Responsable technique"
>>> email = "linda@example.com"

>>> requete = QSqlQuery()
>>> requete.exec(
...     F"" "INSÉRER DANS les contacts (nom, travail, e-mail)
...                 VALEURS ('Nom','emploi','email') "" "
... )
Vrai

Dans cet exemple, vous utilisez une chaîne f pour créer une requête dynamique en interpolant des valeurs spécifiques dans une requête SQL basée sur une chaîne. La requête finale insère des données dans votre Contacts table, qui contient désormais des données sur Linda.

Notez que pour que ce type de requête dynamique fonctionne, vous devez vous assurer que les valeurs à insérer ont le bon type de données. Ainsi, vous utilisez des guillemets simples autour de l'espace réservé dans la chaîne f car ces valeurs doivent être des chaînes.

Exécution de requêtes dynamiques: paramètres d'espace réservé

La deuxième approche pour exécuter des requêtes dynamiques vous oblige à préparer vos requêtes au préalable à l'aide d'un modèle avec espaces réservés pour les paramètres. PyQt prend en charge deux styles d'espace réservé de paramètre:

  1. Style Oracle utilise des espaces réservés nommés tels que :Nom ou :email.
  2. Style ODBC utilise un point d'interrogation (?) en tant qu'espace réservé de position.

Notez que ces styles ne peuvent pas être mélangés dans la même requête. Vous pouvez consulter Approches to Binding Values ​​pour obtenir des exemples supplémentaires sur l'utilisation des espaces réservés.

Pour créer ce type de requête dynamique dans PyQt, vous créez d'abord un modèle avec un espace réservé pour chaque paramètre de requête, puis passez ce modèle en tant qu'argument à .préparer(), qui analyse, compile et prépare le modèle de requête pour l'exécution. Si le modèle a des problèmes, tels qu'une erreur de syntaxe SQL, alors .préparer() ne parvient pas à compiler le modèle et retourne Faux.

Si le processus de préparation réussit, alors préparer() Retour Vrai. Après cela, vous pouvez transmettre une valeur spécifique à chaque paramètre en utilisant .bindValue () avec des paramètres nommés ou positionnels ou en utilisant .addBindValue () avec des paramètres de position. .bindValue () a les deux variantes suivantes:

  1. .bindValue (espace réservé, val)
  2. .bindValue (pos, val)

Dans la première variante, espace réservé représente un espace réservé de style Oracle. Dans la deuxième variante, pos représente un nombre entier de base zéro avec la position d'un paramètre dans la requête. Dans les deux variantes, val contient la valeur à lier à un paramètre spécifique.

.addBindValue () ajoute une valeur à la liste des espaces réservés à l'aide de la liaison positionnelle. Cela signifie que l'ordre des appels à .addBindValue () détermine quelle valeur sera liée à chaque paramètre d'espace réservé dans la requête préparée.

Pour commencer à utiliser des requêtes préparées, vous pouvez préparer un INSÉRER DANS Instruction SQL pour remplir votre base de données avec des exemples de données. Revenez au script que vous avez créé dans la section Exécution de requêtes SQL statiques et ajoutez le code suivant juste après l'appel à impression():

28# Création d'une requête pour une exécution ultérieure en utilisant .prepare ()
29insertDataQuery = QSqlQuery()
30insertDataQuery.préparer(
31    "" "
32                INSÉRER DANS les contacts (
33                                Nom,
34                                emploi,
35                                email
36                )
37                VALEURS (?, ?, ?)
38                "" "
39)
40
41# Exemple de données
42Les données = [[[[
43    («Joe», "Développeur Web Senior", "joe@example.com"),
44    («Lara», "Gestionnaire de projet", "lara@example.com"),
45    («David», "Analyste de données", "david@example.com"),
46    ("Jeanne", "Développeur Python senior", "jane@example.com"),
47]
48
49# Utilisez .addBindValue () pour insérer des données
50pour Nom, emploi, email dans Les données:
51    insertDataQuery.addBindValue(Nom)
52    insertDataQuery.addBindValue(emploi)
53    insertDataQuery.addBindValue(email)
54    insertDataQuery.exec()

La première étape consiste à créer un QSqlQuery objet. Alors tu appelles .préparer() sur l'objet de requête. Dans ce cas, vous utilisez le style ODBC pour les espaces réservés. Votre requête prendra des valeurs pour votre contact Nom, emploi, et email, vous avez donc besoin de trois espaces réservés. Depuis le id column est un nombre entier auto-incrémenté, vous n'avez pas besoin de lui fournir de valeurs.

Ensuite, vous créez des exemples de données pour remplir la base de données. Les données contient une liste de tuples et chaque tuple contient trois éléments: le nom, le travail et l'adresse e-mail de chaque contact.

La dernière étape consiste à lier les valeurs que vous souhaitez transmettre à chaque espace réservé, puis à appeler .exec () pour exécuter la requête. Pour ce faire, vous utilisez un pour boucle. L'en-tête de boucle décompresse chaque tuple dans Les données en trois variables distinctes avec des noms pratiques. Alors tu appelles .addBindValue () sur l'objet de requête pour lier les valeurs aux espaces réservés.

Notez que vous utilisez espaces réservés de position, donc l'ordre dans lequel vous appelez .addBindValue () définira l'ordre dans lequel chaque valeur est passée à l'espace réservé correspondant.

Cette approche de création de requêtes dynamiques est pratique lorsque vous souhaitez personnaliser vos requêtes à l'aide de valeurs provenant de l'entrée de votre utilisateur. Chaque fois que vous utilisez l'entrée de l'utilisateur pour effectuer une requête sur une base de données, vous êtes confronté au risque de sécurité de l'injection SQL.

Dans PyQt, combiner .préparer(), .bindValue (), et .addBindValue () vous protège entièrement contre les attaques par injection SQL, c'est donc la voie à suivre lorsque vous prenez des entrées non fiables pour terminer vos requêtes.

Fermeture et suppression des connexions à la base de données

En pratique, certaines de vos applications PyQt dépendront d'une base de données, et d'autres non. Une application qui dépend d'une base de données crée et ouvre souvent une connexion à la base de données juste avant de créer une fenêtre ou un composant graphique et maintient la connexion ouverte jusqu'à ce que l'application soit fermée.

D'un autre côté, les applications qui ne dépendent pas d'une base de données mais qui utilisent une base de données pour fournir certaines de leurs fonctionnalités se connectent généralement à cette base de données uniquement lorsque cela est nécessaire, voire pas du tout. Dans ces cas, vous pouvez fermer la connexion après utilisation et libérer les ressources associées à cette connexion, telles que la mémoire système.

Pour fermer une connexion dans PyQt, vous appelez .proche() sur la connexion. Cette méthode ferme la connexion et libère toutes les ressources acquises. Il invalide également tout associé QSqlQuery objets car ils ne peuvent pas fonctionner correctement sans connexion active.

Voici un exemple de la façon de fermer une connexion à une base de données active à l'aide de .proche():

>>>

>>> de PyQt5.QtSql importer QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> con.ouvert()
Vrai
>>> con.est ouvert()
Vrai

>>> con.proche()
>>> con.est ouvert()
Faux

Tu peux appeler .proche() sur une connexion pour la fermer et libérer toutes ses ressources associées. Pour vous assurer qu'une connexion est fermée, vous appelez .est ouvert().

Notez que QSqlQuery les objets restent en mémoire après la fermeture de leur connexion associée, vous devez donc rendre vos requêtes inactives en appelant .terminer() ou .clair(), ou en supprimant le QSqlQuery objet avant de fermer la connexion. Sinon, la mémoire résiduelle est laissée de côté dans votre objet de requête.

Vous pouvez rouvrir et réutiliser toute connexion précédemment fermée. C'est parce que .proche() ne supprime pas les connexions de la liste des connexions disponibles, elles restent donc utilisables.

Vous pouvez également supprimer complètement vos connexions à la base de données en utilisant .removeDatabase (). Pour ce faire en toute sécurité, terminez d'abord vos requêtes en utilisant .terminer(), puis fermez la base de données en utilisant .proche(), et enfin supprimez la connexion. Vous pouvez utiliser .removeDatabase (connectionName) pour supprimer la connexion à la base de données appelée connectionName dans la liste des connexions disponibles. Les connexions supprimées ne sont plus disponibles pour une utilisation dans l'application en question.

Pour supprimer la connexion à la base de données par défaut, vous pouvez appeler .connectionName () sur l'objet retourné par .base de données() et transmettez le résultat à .removeDatabase ():

>>>

>>> # La connexion est fermée mais toujours dans la liste des connexions
>>> QSqlDatabase.connectionNames()
['qt_sql_default_connection']

>>> # Supprimer la connexion par défaut
>>> QSqlDatabase.removeDatabase(QSqlDatabase.base de données().connectionName())

>>> # La connexion n'est plus dans la liste des connexions
>>> QSqlDatabase.connectionNames()
[]

>>> # Essayez d'ouvrir une connexion supprimée
>>> con.ouvert()
Faux

Ici, l'appel à .connectionNames () renvoie la liste des connexions disponibles. Dans ce cas, vous n'avez qu'une seule connexion, celle par défaut. Ensuite, vous supprimez la connexion en utilisant .removeDatabase ().

Puisque vous avez besoin d'un nom de connexion à utiliser .removeDatabase (), tu appelles .connectionName () sur le résultat de .base de données() pour obtenir le nom de la connexion par défaut. Enfin, vous appelez .connectionNames () à nouveau pour vous assurer que la connexion ne figure plus dans la liste des connexions disponibles. Essayer d'ouvrir une connexion supprimée reviendra Faux car la connexion n'existe plus.

Affichage et modification des données avec PyQt

Une exigence courante dans les applications GUI qui utilisent des bases de données est la possibilité de charger, d'afficher et de modifier les données de la base de données à l'aide de différents widgets. Les widgets de table, de liste et d'arborescence sont couramment utilisés dans les interfaces graphiques pour gérer les données.

PyQt fournit deux types de widgets différents pour la gestion des données:

  1. Widgets standard inclure des conteneurs internes pour stocker les données.
  2. Afficher les widgets ne gérez pas de conteneurs de données internes mais utilisez des modèles pour accéder aux données.

Pour les petites applications GUI qui gèrent de petites bases de données, vous pouvez utiliser la première approche. La deuxième approche est pratique lorsque vous créez des applications GUI complexes qui gèrent de grandes bases de données.

La deuxième approche tire parti de la programmation Model-View de PyQt. Avec cette approche, vous disposez de widgets qui représentent des vues telles que des tables, des listes et des arbres d'une part et des classes de modèles qui communiquent avec vos données d'autre part.

Comprendre l'architecture Model-View de PyQt

Le modèle de conception Model-View-Controller (MVC) est un modèle logiciel général destiné à diviser le code d’une application en trois couches générales, chacune ayant un rôle différent.

le modèle prend en charge la logique métier de l'application, le vue fournit des représentations à l'écran et le manette relie le modèle et la vue pour faire fonctionner l'application.

Qt fournit une variante personnalisée de MVC. Ils l'appellent l'architecture Model-View, et elle est également disponible pour PyQt. Le modèle sépare également la logique en trois composants:

  1. Des modèles communiquer et accéder aux données. Ils définissent également une interface qui est utilisée par les vues et les délégués pour accéder aux données. Tous les modèles sont basés sur QAbstractItemModel. Certains modèles couramment utilisés incluent QStandardItemModel, QFileSystemModelet les modèles liés à SQL.

  2. Vues sont responsables de l'affichage des données à l'utilisateur. Ils ont également des fonctionnalités similaires à celles du contrôleur dans le modèle MVC. Toutes les vues sont basées sur QAbstractItemView. Certaines vues couramment utilisées sont QListView, QTableView, et QTreeView.

  3. Délégués peignez les éléments de vue et fournissez des widgets d'édition pour modifier les éléments. Ils communiquent également avec le modèle si un élément a été modifié. La classe de base est QAbstractItemDelegate.

La séparation des classes en ces trois composants implique que les modifications apportées aux modèles seront automatiquement répercutées sur les vues ou widgets associés, et les modifications apportées aux vues ou aux widgets via des délégués mettront à jour automatiquement le modèle sous-jacent.

De plus, vous pouvez afficher les mêmes données dans différentes vues sans avoir besoin de plusieurs modèles.

Utilisation des classes de widgets standard

PyQt fournit un tas de widgets standard pour afficher et modifier les données dans vos applications GUI. Ces widgets standard fournissent des vues telles que des tables, des arborescences et des listes. Ils fournissent également un conteneur interne pour stocker les données et des délégués pratiques pour éditer les données. Toutes ces fonctionnalités sont regroupées dans une seule classe.

Voici trois de ces classes standard:

QTableWidget est sans doute le widget le plus populaire pour l'affichage et l'édition de données. Il crée un tableau 2D de QTableWidgetItem objets. Chaque élément contient une valeur individuelle sous forme de chaîne. Toutes ces valeurs sont affichées et organisées dans un tableau de lignes et de colonnes.

Vous pouvez effectuer au moins les opérations suivantes sur un QTableWidget objet:

Voici un exemple d'application qui montre comment utiliser un QTableWidget objet pour afficher les données dans une interface graphique. L'application utilise la base de données que vous avez créée et remplie dans les sections précédentes, donc si vous souhaitez l'exécuter, vous devez enregistrer le code dans le même répertoire dans lequel vous avez le contacts.sqlite base de données:

Exemple de QTableWidget

Si vous double-cliquez sur une cellule du tableau, vous pourrez modifier le contenu de la cellule. Cependant, vos modifications ne seront pas enregistrées dans votre base de données.

Voici le code de votre application:

    1importer sys
    2
    3de PyQt5.QtSql importer QSqlDatabase, QSqlQuery
    4de PyQt5.QtWidgets importer (
    5    QApplication,
    6    QMainWindow,
    sept    QMessageBox,
    8    QTableWidget,
    9    QTableWidgetItem,
dix)
11
12classe Contacts(QMainWindow):
13    def __init__(soi, parent=Aucun):
14        super().__init__(parent)
15        soi.setWindowTitle("Exemple QTableView")
16        soi.redimensionner(450, 250)
17        # Configurer la vue et charger les données
18        soi.vue = QTableWidget()
19        soi.vue.setColumnCount(4)
20        soi.vue.setHorizontalHeaderLabels([[[["ID", "Nom", "Emploi", "Email"])
21        requete = QSqlQuery("CHOISIR l'identifiant, le nom, le poste, l'e-mail FROM contacts")
22        tandis que requete.prochain():
23            Lignes = soi.vue.rowCount()
24            soi.vue.setRowCount(Lignes + 1)
25            soi.vue.setItem(Lignes, 0, QTableWidgetItem(str(requete.valeur(0))))
26            soi.vue.setItem(Lignes, 1, QTableWidgetItem(requete.valeur(1)))
27            soi.vue.setItem(Lignes, 2, QTableWidgetItem(requete.valeur(2)))
28            soi.vue.setItem(Lignes, 3, QTableWidgetItem(requete.valeur(3)))
29        soi.vue.resizeColumnsToContents()
30        soi.setCentralWidget(soi.vue)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    si ne pas con.ouvert():
36        QMessageBox.critique(
37            Aucun,
38            "Exemple QTableView - Erreur!",
39            "Erreur de la base de données: % s" % con.lastError().databaseText(),
40        )
41        revenir Faux
42    revenir Vrai
43
44app = QApplication(sys.argv)
45si ne pas createConnection():
46    sys.sortie(1)
47gagner = Contacts()
48gagner.montrer()
49sys.sortie(app.exec_())

Voici ce qui se passe dans cet exemple:

  • Lignes 18 à 20 créer un QTableWidget objet, définissez le nombre de colonnes sur 4et définissez des libellés conviviaux pour l'en-tête de chaque colonne.
  • Ligne 21 crée et exécute un SÉLECTIONNER Requête SQL sur votre base de données pour obtenir toutes les données du Contacts table.
  • Ligne 22 commence un tandis que boucle pour parcourir les enregistrements dans le résultat de la requête en utilisant .prochain().
  • Ligne 24 incrémente le nombre de lignes du tableau de 1 en utilisant .setRowCount ().
  • Lignes 25 à 28 ajouter des éléments de données à votre tableau en utilisant .setItem (). Notez que puisque les valeurs du id les colonnes sont des nombres entiers, vous devez les convertir en chaînes pour pouvoir les stocker dans un QTableWidgetItem objet.

.setItem () prend trois arguments:

  1. rangée contient un entier de base zéro qui représente l'index d'une ligne donnée dans la table.
  2. colonne contient un entier de base zéro qui représente l'index d'une colonne donnée dans la table.
  3. article détient le QTableWidgetItem objet que vous devez placer dans une cellule donnée du tableau.

Enfin, vous appelez .resizeColumnsToContents () sur votre vue pour ajuster la taille des colonnes à leur contenu et offrir un meilleur rendu des données.

L'affichage et la modification des tables de base de données à l'aide de widgets standard peuvent devenir une tâche difficile. C'est parce que vous aurez deux copies des mêmes données. En d'autres termes, vous disposerez d'une copie des données à deux emplacements:

  1. En dehors du widget, dans votre base de données
  2. À l'intérieur du widget, dans les conteneurs internes du widget

Vous êtes responsable de la synchronisation manuelle des deux copies de vos données, ce qui peut être une opération ennuyeuse et sujette à des erreurs. Heureusement, vous pouvez utiliser l'architecture Model-View de PyQt pour éviter la plupart de ces problèmes, comme vous le verrez dans la section suivante.

Utilisation des classes de vue et de modèle

Les classes Model-View de PyQt éliminent les problèmes de duplication et de synchronisation des données qui peuvent survenir lorsque vous utilisez des classes de widget standard pour créer des applications de base de données. L'architecture Model-View vous permet d'utiliser plusieurs vues pour afficher les mêmes données car vous pouvez transmettre un modèle à plusieurs vues.

Les classes de modèle fournissent une interface de programmation d'application (API) que vous pouvez utiliser pour manipuler des données. Les classes View fournissent des objets délégués pratiques que vous pouvez utiliser pour modifier directement les données dans la vue. Pour connecter une vue à un module donné, vous devez appeler .setModel () sur l'objet de vue.

PyQt propose un ensemble de classes de vue qui prennent en charge l'architecture Model-View:

Voir la classe Affiche
QListView Une liste d'éléments qui prennent des valeurs directement à partir d'une classe de modèle
QTreeView Une arborescence hiérarchique d'éléments qui prennent des valeurs directement à partir d'une classe de modèle
QTableView Une table d'éléments qui prennent des valeurs directement à partir d'une classe de modèle

Vous pouvez utiliser ces classes de vue avec des classes de modèle pour créer vos applications de base de données. Cela rendra vos applications plus robustes, plus rapides à coder et moins sujettes aux erreurs.

Voici quelques-unes des classes de modèle fournies par PyQt pour travailler avec des bases de données SQL:

Classe de modèle La description
QSqlQueryModel Un modèle de données en lecture seule pour les requêtes SQL
QSqlTableModel Un modèle de données modifiable pour lire et écrire des enregistrements dans une seule table
QSqlRelationalTableModel Un modèle de données modifiable pour lire et écrire des enregistrements dans une table relationnelle

Une fois que vous avez connecté l'un de ces modèles à une table ou une requête de base de données physique, vous pouvez les utiliser pour remplir vos vues. Les vues fournissent des objets délégués qui vous permettent de modifier les données directement dans la vue. Le modèle connecté à la vue mettra à jour les données de votre base de données pour refléter tout changement dans la vue. Notez que vous n'avez pas à mettre à jour manuellement les données de la base de données. Le modèle le fera pour vous.

Voici un exemple qui montre les bases de l’utilisation d’un QTableView objet et un QSqlTableModel objet ensemble pour créer une application de base de données à l'aide de l'architecture Model-View de PyQt:

Exemple de QTableView

Pour modifier les données dans une cellule du tableau, vous pouvez double-cliquer sur la cellule. Un widget délégué pratique s'affichera dans la cellule, vous permettant de modifier son contenu. Ensuite, vous pouvez frapper Entrer pour enregistrer les modifications.

La possibilité de gérer et d’enregistrer automatiquement les modifications des données est l’un des avantages les plus importants de l’utilisation des classes Model-View de PyQt. L'architecture Model-View améliorera votre productivité et réduira les erreurs qui peuvent apparaître lorsque vous devez écrire vous-même du code de manipulation de données.

Voici le code pour créer l'application:

    1importer sys
    2
    3de PyQt5.QtCore importer Qt
    4de PyQt5.QtSql importer QSqlDatabase, QSqlTableModel
    5de PyQt5.QtWidgets importer (
    6    QApplication,
    sept    QMainWindow,
    8    QMessageBox,
    9    QTableView,
dix)
11
12classe Contacts(QMainWindow):
13    def __init__(soi, parent=Aucun):
14        super().__init__(parent)
15        soi.setWindowTitle("Exemple QTableView")
16        soi.redimensionner(415, 200)
17        # Configurer le modèle
18        soi.modèle = QSqlTableModel(soi)
19        soi.modèle.setTable("Contacts")
20        soi.modèle.setEditStrategy(QSqlTableModel.OnFieldChange)
21        soi.modèle.setHeaderData(0, Qt.Horizontal, "ID")
22        soi.modèle.setHeaderData(1, Qt.Horizontal, "Nom")
23        soi.modèle.setHeaderData(2, Qt.Horizontal, "Emploi")
24        soi.modèle.setHeaderData(3, Qt.Horizontal, "Email")
25        soi.modèle.sélectionner()
26        # Configurer la vue
27        soi.vue = QTableView()
28        soi.vue.setModel(soi.modèle)
29        soi.vue.resizeColumnsToContents()
30        soi.setCentralWidget(soi.vue)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    si ne pas con.ouvert():
36        QMessageBox.critique(
37            Aucun,
38            "Exemple QTableView - Erreur!",
39            "Erreur de la base de données: % s" % con.lastError().databaseText(),
40        )
41        revenir Faux
42    revenir Vrai
43
44app = QApplication(sys.argv)
45si ne pas createConnection():
46    sys.sortie(1)
47gagner = Contacts()
48gagner.montrer()
49sys.sortie(app.exec_())

Voici ce qui se passe dans ce code:

  • Ligne 18 crée un modifiable QSqlTableModel objet.
  • Ligne 19 connecte votre modèle avec le Contacts table dans votre base de données en utilisant .setTable ().
  • Ligne 20 définit la stratégie d'édition du modèle sur OnFieldChange. Cette stratégie permet au modèle de mettre à jour automatiquement les données de votre base de données si l'utilisateur modifie l'une des données directement dans la vue.
  • Lignes 21 à 24 définir des étiquettes conviviales sur les en-têtes horizontaux du modèle en utilisant .setHeaderData ().
  • Ligne 25 charge les données de votre base de données et remplit le modèle en appelant .sélectionner().
  • Ligne 27 crée l'objet de vue de table pour afficher les données contenues dans le modèle.
  • Ligne 28 connecte la vue au modèle en appelant .setModel () sur la vue avec votre modèle de données comme argument.
  • Ligne 29 appels .resizeColumnsToContents () sur l'objet de vue pour ajuster le tableau à son contenu.

C'est ça! Vous disposez désormais d'une application de base de données entièrement fonctionnelle.

Utilisation de bases de données SQL dans PyQt: bonnes pratiques

Lorsqu'il s'agit d'utiliser efficacement la prise en charge SQL de PyQt, il existe quelques bonnes pratiques que vous souhaiterez peut-être utiliser dans vos applications:

  • Favoriser le support SQL de PyQt sur la bibliothèque standard Python ou sur des bibliothèques tierces pour profiter de l'intégration naturelle de ces classes avec le reste des classes et de l'infrastructure de PyQt, principalement avec l'architecture Model-View.

  • Utiliser des requêtes dynamiques préalablement préparées avec des espaces réservés pour les paramètres et liez les valeurs à ces paramètres en utilisant .addBindValue () et .bindValue (). Cela aidera à prévenir les attaques par injection SQL.

  • Gérer les erreurs qui peuvent se produire lors de l'ouverture d'une connexion à une base de données pour éviter des comportements inattendus et des plantages d'application.

  • Fermer et supprimer les connexions et requêtes de base de données inutiles pour libérer toutes les ressources système acquises.

  • Minimisez l'utilisation de SÉLECTIONNER * requêtes pour éviter les problèmes lors de la récupération de données avec .valeur().

  • Passez vos mots de passe à .ouvert() au lieu de .setPassword () pour éviter le risque de compromettre votre sécurité.

  • Tirez parti de l'architecture Model-View de PyQt et son intégration avec le support SQL de PyQt pour rendre vos applications plus robustes.

Cette liste n'est pas complète, mais elle vous aidera à mieux utiliser la prise en charge SQL de PyQt lors du développement de vos applications de base de données.

Conclusion

L'utilisation de la prise en charge intégrée de PyQt pour travailler avec des bases de données SQL est une compétence importante pour tout développeur Python qui crée des applications d'interface graphique PyQt et doit les connecter à une base de données. PyQt fournit un ensemble cohérent de classes pour la gestion des bases de données SQL.

Ces classes s’intègrent entièrement à l’architecture Model-View de PyQt, ce qui vous permet de développer des applications GUI capables de gérer des bases de données de manière conviviale.

Dans ce didacticiel, vous avez appris à:

  • Utilisez PyQt Prise en charge SQL se connecter à une base de données
  • Exécuter Requêtes SQL sur une base de données avec PyQt
  • Créez des applications de base de données à l'aide de PyQt Architecture Model-View
  • Afficher et modifier les données d'une base de données à l'aide de PyQt widgets

Grâce à ces connaissances, vous pouvez améliorer votre productivité lors de la création d'applications de base de données non triviales et rendre vos applications GUI plus robustes.

[ad_2]