Database
 sql >> Base de données >  >> RDS >> Database

Gérer les bases de données SQL avec PyQt :les bases

La création d'applications qui utilisent 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 graphique, 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.

Le support 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.

Dans ce didacticiel, vous apprendrez à :

  • Utiliser le support SQL de PyQt pour se connecter de manière fiable à une base de données
  • Exécuter des requêtes SQL sur une base de données utilisant PyQt
  • Utiliser l'architecture Model-View de PyQt dans les applications de base de données
  • Afficher et modifier des données à l'aide de différents widgets PyQt

Les exemples de ce tutoriel 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 d'interface graphique avec Python et PyQt sera également utile.

Bonus gratuit : 5 Thoughts On Python Mastery, un cours gratuit pour les développeurs Python qui vous montre la feuille de route et l'état d'esprit dont vous aurez besoin pour faire passer vos compétences Python au niveau supérieur.


Connecter PyQt à une base de données SQL

Connecter une application à une base de données relationnelle et faire en sorte que l'application crée, lise, mette à jour et supprime 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 tables , ou relations . Une ligne donnée dans un tableau est appelée enregistrement ou uplet , et une colonne est appelée attribut .

Remarque : Le terme champ est couramment utilisé pour identifier une seule donnée stockée dans une cellule d'un enregistrement donné dans une table. D'autre part, le terme nom de champ est utilisé pour identifier le nom d'une colonne.

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 maintenir 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 base de données différents, tels que SQLite, PostgreSQL, MySQL, MariaDB et bien d'autres. Vous pouvez connecter Python à n'importe lequel de ces systèmes de base de données à l'aide d'une bibliothèque Python SQL dédiée.

Remarque : Même si le support SQL intégré de PyQt est l'option préférée pour gérer les bases de données SQL dans PyQt, vous pouvez également utiliser n'importe quelle autre bibliothèque pour gérer la connexion à la base de données. Certaines de ces bibliothèques incluent SQLAlchemy, pandas, SQLite, etc.

Cependant, l'utilisation d'une bibliothèque différente pour gérer vos bases de données présente certains inconvénients. Vous ne pourrez pas profiter de l'intégration entre les classes SQL de PyQt et l'architecture Model-View. De plus, vous ajouterez des dépendances supplémentaires à votre application.

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.

Remarque : Malheureusement, la documentation officielle de PyQt5 contient des sections incomplètes. Pour contourner ce problème, vous pouvez consulter la documentation de PyQt4, la documentation de Qt pour Python ou la documentation originale de Qt. Dans ce didacticiel, certains liens vous amènent à la documentation originale de Qt, qui est une meilleure source d'informations dans la plupart des cas.

Dans ce didacticiel, vous apprendrez les bases de l'utilisation du support SQL de PyQt pour créer des applications graphiques 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, qui est un système de base de données bien testé prenant en charge 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 séparé. Cela en fait une option conviviale pour apprendre le 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 également 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 à une base de données en utilisant 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, appelez simplement .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(
    driver, connectionName=QSqlDatabase.defaultConnection
)

Le premier argument, driver , est un argument obligatoire qui contient une chaîne contenant le nom d'un pilote SQL pris en charge par PyQt. Le deuxième argument, connectionName , est un argument facultatif qui contient 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 , alors 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 à une base de données à une liste de connexions disponibles. Cette liste est un registre mondial que PyQt maintient en coulisses pour garder une trace des connexions disponibles dans une application. Enregistrer vos connexions avec un connectionName significatif 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 Description
.setDatabaseName(name) Définit le nom de la base de données sur name , qui est une chaîne représentant un nom de base de données valide
.setHostName(host) Définit le nom d'hôte sur host , qui est une chaîne représentant un nom d'hôte valide
.setUserName(username) Définit le nom d'utilisateur sur username , qui est une chaîne représentant un nom d'utilisateur valide
.setPassword(password) Définit le mot de passe sur password , 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é ultérieurement en appelant .password() . Il s'agit d'un risque de sécurité sérieux que vous devez éviter d'introduire dans vos applications de bases de données. Vous apprendrez une approche plus sûre dans la section Ouvrir 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 :

>>>
>>> from PyQt5.QtSql import QSqlDatabase

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

>>> con
<PyQt5.QtSql.QSqlDatabase object at 0x7f0facec0c10>

>>> con.databaseName()
'contacts.sqlite'

>>> con.connectionName()
'qt_sql_default_connection'

Ce code va créer un objet de connexion à la base de données en utilisant "QSQLITE" comme pilote de connexion et "contacts.sqlite" comme nom de base de données de la connexion. Puisque vous ne transmettez pas de nom de connexion à .addDatabase() , celle qui vient d'être 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 ":memory:" pour une base de données en mémoire.



Gérer plusieurs connexions

Dans certaines situations, vous devrez peut-être utiliser plusieurs connexions à une seule base de données. Par exemple, vous souhaiterez peut-être consigner les interactions des utilisateurs avec la base de données à l'aide d'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 donner des noms spécifiques à vos différentes connexions et référencer chaque connexion par son nom. Si vous souhaitez donner un nom à votre connexion à la base de données, transmettez ce nom comme deuxième argument à .addDatabase() :

>>>
>>> from PyQt5.QtSql import QSqlDatabase

>>> # First connection
>>> con1 = QSqlDatabase.addDatabase("QSQLITE", "con1")
>>> con1.setDatabaseName("contacts.sqlite")

>>> # Second connection
>>> con2 = QSqlDatabase.addDatabase("QSQLITE", "con2")
>>> con2.setDatabaseName("contacts.sqlite")

>>> con1
<PyQt5.QtSql.QSqlDatabase object at 0x7f367f5fbf90>
>>> con2
<PyQt5.QtSql.QSqlDatabase object at 0x7f3686dd7510>

>>> con1.databaseName()
'contacts.sqlite'
>>> con2.databaseName()
'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 .database() avec un nom de connexion :

>>>
>>> from PyQt5.QtSql import QSqlDatabase

>>> db = QSqlDatabase.database("con1", open=False)

>>> db.databaseName()
'contacts.sqlite'
>>> db.connectionName()
'con1'

Dans cet exemple, vous voyez que .database() 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. open contient une valeur booléenne qui indique .database() si vous souhaitez ouvrir automatiquement la connexion ou non. Si open est True (valeur par défaut) et que la connexion n'est pas ouverte, la connexion s'ouvre automatiquement.

La valeur de retour de .database() 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 du pilote SQLite . Ce n'est pas le seul pilote disponible dans PyQt. La bibliothèque fournit un riche ensemble 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 pilote 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 les chaînes d'identification 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 databaseName , hostName , userName , et password , 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 ce faire, vous appelez .open() sur l'objet de connexion. .open() a les deux variantes suivantes :

  1. .open() ouvre une connexion à la base de données en utilisant les valeurs de connexion actuelles.
  2. .open(username, password) ouvre une connexion à la base de données en utilisant le username fourni et password .

Les deux variantes renvoient True si la connexion est réussie. Sinon, ils renvoient False . 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.

Remarque : Comme vous l'avez appris auparavant, .setPassword(password) stocke les mots de passe sous forme de texte brut, ce qui constitue un risque pour la sécurité. Par contre, .open() ne stocke pas du tout les mots de passe. Il transmet le mot de passe directement au conducteur lors de l'ouverture de la connexion. Après cela, il supprime le mot de passe. Donc, en utilisant .open() gérer vos mots de passe est la solution si vous souhaitez éviter les problèmes de sécurité.

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

>>>
>>> from PyQt5.QtSql import QSqlDatabase

>>> # Create the connection
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Open the connection
>>> con.open()
True
>>> con.isOpen()
True

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

Remarque : Si vous appelez .open() sur une connexion qui utilise le pilote SQLite et que le fichier de base de données n'existe pas, un nouveau fichier de base de données vide sera créé automatiquement.

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 manière courante d'appeler .open() est de l'envelopper dans une instruction conditionnelle. Cela permet de gérer les erreurs qui peuvent survenir lors de l'ouverture de la connexion :

>>>
>>> import sys
>>> from PyQt5.QtSql import QSqlDatabase

>>> # Create the connection
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Open the connection and handle errors
>>> if not con.open():
...     print("Unable to connect to the database")
...     sys.exit(1)

Emballage de l'appel à .open() 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 .open() dans une session interactive, vous utilisez donc print() présenter des messages d'erreur aux utilisateurs. Cependant, dans les applications GUI, au lieu d'utiliser print() , vous utilisez normalement une 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 graphique illustrant une manière de gérer les erreurs de connexion :

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase
 4from PyQt5.QtWidgets import QApplication, QMessageBox, QLabel
 5
 6# Create the connection
 7con = QSqlDatabase.addDatabase("QSQLITE")
 8con.setDatabaseName("/home/contacts.sqlite")
 9
10# Create the application
11app = QApplication(sys.argv)
12
13# Try to open the connection and handle possible errors
14if not con.open():
15    QMessageBox.critical(
16        None,
17        "App Name - Error!",
18        "Database Error: %s" % con.lastError().databaseText(),
19    )
20    sys.exit(1)
21
22# Create the application's window
23win = QLabel("Connection Successfully Opened!")
24win.setWindowTitle("App Name")
25win.resize(200, 100)
26win.show()
27sys.exit(app.exec_())

Le if l'instruction à la ligne 14 vérifie si la connexion a échoué. Si le /home/ répertoire n'existe pas ou si vous n'avez pas la permission d'y écrire, alors l'appel à .open() échoue car le fichier de base de données ne peut pas être créé. Dans cette situation, le flux d'exécution entre le if 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, alors l'appel à .open() réussira et vous verrez une fenêtre affichant le message Connection Successfully Opened! Vous aurez également un nouveau fichier de base de données appelé contacts.sqlite dans le répertoire sélectionné.

Notez que vous passez None en tant que parent du message car, au moment d'afficher le 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écuter des requêtes SQL avec PyQt

Avec une connexion à une 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 en langage de manipulation de données (DML), telles que SELECT , INSERT , UPDATE , et DELETE , ainsi que des instructions en langage de définition de données (DDL), telles que CREATE TABLE et ainsi de suite.

Le constructeur de QSqlQuery a plusieurs variantes, mais dans ce tutoriel, vous allez en découvrir deux :

  1. QSqlQuery(query, connection) construit un objet de requête à l'aide d'une query SQL basée sur une chaîne et une connection à la base de données . 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 query n'est pas une chaîne vide, elle sera exécutée immédiatement.

  2. QSqlQuery(connection) construit un objet de requête en utilisant connection . Si connection 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 requête. Vous pouvez utiliser .exec() de deux manières différentes :

  1. .exec(query) exécute la requête SQL basée sur une chaîne contenue dans query . Il renvoie True si la requête a réussi et sinon renvoie False .

  2. .exec() exécute une requête SQL préalablement préparée. Il renvoie True si la requête a réussi et sinon renvoie False .

Remarque : PyQt implémente également des variantes de QSqlQuery.exec() avec le nom .exec_() . Celles-ci fournissent une rétrocompatibilité avec les anciennes versions de Python dans lesquelles exec était un mot-clé de la langue.

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


Exécuter des 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 :

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase, QSqlQuery
 4
 5# Create the connection
 6con = QSqlDatabase.addDatabase("QSQLITE")
 7con.setDatabaseName("contacts.sqlite")
 8
 9# Open the connection
10if not con.open():
11    print("Database Error: %s" % con.lastError().databaseText())
12    sys.exit(1)
13
14# Create a query and execute it right away using .exec()
15createTableQuery = QSqlQuery()
16createTableQuery.exec(
17    """
18    CREATE TABLE contacts (
19        id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
20        name VARCHAR(40) NOT NULL,
21        job VARCHAR(50),
22        email VARCHAR(40) NOT NULL
23    )
24    """
25)
26
27print(con.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 transmettant une requête SQL basée sur une chaîne en tant qu'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 aura les quatre colonnes suivantes :

Colonne Contenu
id Un entier avec la clé primaire de la table
name Une chaîne avec le nom d'un contact
job Une chaîne avec le titre du poste d'un contact
email Une chaîne avec l'e-mail 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 remarquerez qu'un nouveau fichier de base de données appelé contacts.sqlite est créé dans votre répertoire courant. 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.

Remarque : Une requête SQL basée sur une chaîne doit utiliser une syntaxe appropriée en fonction de la base de données SQL spécifique que vous interrogez. Si la syntaxe est incorrecte, alors .exec() ignore la requête et renvoie False .

Dans le cas de SQLite, la requête ne peut contenir qu'une seule instruction à la fois.

Appel de .exec() sur une QSqlQuery 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 les paramètres , de sorte que 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 le formatage des chaînes 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 aux paramètres.

La première approche 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 pourriez être confronté à des attaques par injection SQL.

Voici un exemple d'utilisation du formatage de chaîne pour créer des requêtes dynamiques dans PyQt :

>>>
>>> from PyQt5.QtSql import QSqlQuery, QSqlDatabase

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

>>> name = "Linda"
>>> job = "Technical Lead"
>>> email = "[email protected]"

>>> query = QSqlQuery()
>>> query.exec(
...     f"""INSERT INTO contacts (name, job, email)
...     VALUES ('{name}', '{job}', '{email}')"""
... )
True

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 vos contacts table, qui contient maintenant des données sur Linda .

Remarque : Plus loin dans ce didacticiel, vous verrez comment récupérer et parcourir les données stockées dans une base de données.

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 nécessite de préparer vos requêtes au préalable à l'aide d'un modèle avec des 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 :name ou :email .
  2. Style ODBC utilise un point d'interrogation (? ) comme 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 des valeurs contraignantes pour des exemples supplémentaires sur la façon d'utiliser les espaces réservés.

Remarque : ODBC signifie Open Database Connectivity.

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

Si le processus de préparation réussit, alors prepare() renvoie True . Après cela, vous pouvez passer 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(placeholder, val)
  2. .bindValue(pos, val)

Dans la première variante, placeholder représente un espace réservé de style Oracle. Dans la seconde 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 INSERT INTO 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écuter des requêtes SQL statiques et ajoutez le code suivant juste après l'appel à print() :

28# Creating a query for later execution using .prepare()
29insertDataQuery = QSqlQuery()
30insertDataQuery.prepare(
31    """
32    INSERT INTO contacts (
33        name,
34        job,
35        email
36    )
37    VALUES (?, ?, ?)
38    """
39)
40
41# Sample data
42data = [
43    ("Joe", "Senior Web Developer", "[email protected]"),
44    ("Lara", "Project Manager", "[email protected]"),
45    ("David", "Data Analyst", "[email protected]"),
46    ("Jane", "Senior Python Developer", "[email protected]"),
47]
48
49# Use .addBindValue() to insert data
50for name, job, email in data:
51    insertDataQuery.addBindValue(name)
52    insertDataQuery.addBindValue(job)
53    insertDataQuery.addBindValue(email)
54    insertDataQuery.exec()

La première étape consiste à créer un QSqlQuery objet. Ensuite, vous appelez .prepare() sur l'objet requête. Dans ce cas, vous utilisez le style ODBC pour les espaces réservés. Votre requête prendra des valeurs pour le name de votre contact , job , et email , vous avez donc besoin de trois espaces réservés. Depuis le id colonne 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. data contient une liste de tuples, et chaque tuple contient trois éléments :le nom, le travail et l'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 for boucle. L'en-tête de la boucle décompresse chaque tuple dans data en trois variables distinctes avec des noms pratiques. Ensuite, vous appelez .addBindValue() sur l'objet de requête pour lier les valeurs aux espaces réservés.

Notez que vous utilisez des espaces réservés de position , so the order in which you call .addBindValue() will define the order in which each value is passed to the corresponding placeholder.

This approach for creating dynamic queries is handy when you want to customize your queries using values that come from your user’s input. Anytime you take the user’s input to complete a query on a database, you face the security risk of SQL injection.

In PyQt, combining .prepare() , .bindValue() , and .addBindValue() fully protects you against SQL injection attacks, so this is the way to go when you’re taking untrusted input to complete your queries.



Navigating the Records in a Query

If you execute a SELECT statement, then your QSqlQuery object will retrieve zero or more records from one or more tables in your database. The query will hold records containing data that matches the query’s criteria. If no data matches the criteria, then your query will be empty.

QSqlQuery provides a set of navigation methods that you can use to move throughout the records in a query result:

Méthode Retrieves
.next() The next record
.previous() The previous record
.first() The first record
.last() The last record
.seek(index, relative=False) The record at position index

All these methods position the query object on the retrieved record if that record is available. Most of these methods have specific rules that apply when using them. With these methods, you can move forward, backward, or arbitrarily through the records in a query result. Since they all return either True or False , you can use them in a while loop to navigate all the records in one go.

These methods work with active queries . A query is active when you’ve successfully run .exec() on it, but the query isn’t finished yet. Once an active query is on a valid record, you can retrieve data from that record using .value(index) . This method takes a zero-based integer number, index , and returns the value at that index (column) in the current record.

Remarque : If you execute a SELECT * type of query, then the columns in the result won’t follow a known order. This might cause problems when you use .value() to retrieve the value at a given column because there’s no way of knowing if you’re using the right column index.

You’ll look at a few examples of how to use some of the navigation methods to move throughout a query below. But first, you need to create a connection to your database:

>>>
>>> from PyQt5.QtSql import QSqlDatabase, QSqlQuery

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

Here, you create and open a new connection to contacts.sqlite . If you’ve been following along with this tutorial so far, then this database already contains some sample data. Now you can create a QSqlQuery object and execute it on that data:

>>>
>>> # Create and execute a query
>>> query = QSqlQuery()
>>> query.exec("SELECT name, job, email FROM contacts")
True

This query retrieves data about the name , job , and email of all the contacts stored in the contacts table. Since .exec() returned True , the query was successful and is now an active query. You can navigate the records in this query using any of the navigation methods you saw before. You can also retrieve the data at any column in a record using .value() :

>>>
>>> # First record
>>> query.first()
True

>>> # Named indices for readability
>>> name, job, email = range(3)

>>> # Retrieve data from the first record
>>> query.value(name)
'Linda'

>>> # Next record
>>> query.next()
True
>>> query.value(job)
'Senior Web Developer'

>>> # Last record
>>> query.last()
True
>>> query.value(email)
'[email protected]'

With the navigation methods, you can move around the query result. With .value() , you can retrieve the data at any column in a given record.

You can also iterate through all the records in your query using a while loop along with .next() :

>>>
>>> query.exec()
True

>>> while query.next():
...     print(query.value(name), query.value(job), query.value(email))
...
Linda Technical Lead [email protected]
Joe Senior Web Developer [email protected]
...

With .next() , you navigate all the records in a query result. .next() works similar to the iterator protocol in Python. Once you’ve iterated over the records in a query result, .next() starts returning False until you run .exec() de nouveau. A call to .exec() retrieves data from a database and places the query object’s internal pointer one position before the first record, so when you call .next() , you get the first record again.

You can also loop in reverse order using .previous() :

>>>
>>> while query.previous():
...     print(query.value(name), query.value(job), query.value(email))
...
Jane Senior Python Developer [email protected]
David Data Analyst [email protected]
...

.previous() works similar to .next() , but the iteration is done in reverse order. In other words, the loop goes from the query pointer’s position back to the first record.

Sometimes you might want to get the index that identifies a given column in a table by using the name of that column. To do that, you can call .indexOf() on the return value of .record() :

>>>
>>> query.first()
True

>>> # Get the index of name
>>> name = query.record().indexOf("name")

>>> query.value(name)
'Linda'

>>> # Finish the query object if unneeded
>>> query.finish()
>>> query.isActive()
False

The call to .indexOf() on the result of .record() returns the index of the "name" colonne. If "name" doesn’t exist, then .indexOf() renvoie -1 . This is handy when you use a SELECT * statement in which the order of columns is unknown. Finally, if you’re done with a query object, then you can turn it inactive by calling .finish() . This will free the system memory associated with the query object at hand.




Closing and Removing Database Connections

In practice, some of your PyQt applications will depend on a database, and others won’t. An application that depends on a database often creates and opens a database connection just before creating any window or graphical component and keeps the connection open until the application is closed.

On the other hand, applications that don’t depend on a database but use a database to provide some of their functionalities typically connect to that database only when needed, if at all. In these cases, you can close the connection after use and free the resources associated with that connection, such as system memory.

To close a connection in PyQt, you call .close() on the connection. This method closes the connection and frees any acquired resources. It also invalidates any associated QSqlQuery objects because they can’t work properly without an active connection.

Here’s an example of how to close an active database connection using .close() :

>>>
>>> from PyQt5.QtSql import QSqlDatabase

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

>>> con.open()
True
>>> con.isOpen()
True

>>> con.close()
>>> con.isOpen()
False

You can call .close() on a connection to close it and free all its associated resources. To make sure that a connection is closed, you call .isOpen() .

Note that QSqlQuery objects remain in memory after closing their associated connection, so you must make your queries inactive by calling .finish() or .clear() , or by deleting the QSqlQuery object before closing the connection. Otherwise, residual memory is left out in your query object.

You can reopen and reuse any previously closed connection. That’s because .close() doesn’t remove connections from the list of available connections, so they remain usable.

You can also completely remove your database connections using .removeDatabase() . To do this safely, first finish your queries using .finish() , then close the database using .close() , and finally remove the connection. You can use .removeDatabase(connectionName) to remove the database connection called connectionName from the list of available connections. Removed connections are no longer available for use in the application at hand.

To remove the default database connection, you can call .connectionName() on the object returned by .database() and pass the result to .removeDatabase() :

>>>
>>> # The connection is closed but still in the list of connections
>>> QSqlDatabase.connectionNames()
['qt_sql_default_connection']

>>> # Remove the default connection
>>> QSqlDatabase.removeDatabase(QSqlDatabase.database().connectionName())

>>> # The connection is no longer in the list of connections
>>> QSqlDatabase.connectionNames()
[]

>>> # Try to open a removed connection
>>> con.open()
False

Here, the call to .connectionNames() returns the list of available connections. In this case, you have only one connection, the default. Then you remove the connection using .removeDatabase() .

Remarque : Before closing and removing a database connection, you need to make sure that everything that uses the connection is deleted or set to use a different data source. Otherwise, you can have a resource leak .

Since you need a connection name to use .removeDatabase() , you call .connectionName() on the result of .database() to get the name of the default connection. Finally, you call .connectionNames() again to make sure that the connection is no longer in the list of available connections. Trying to open a removed connection will return False because the connection no longer exists.



Displaying and Editing Data With PyQt

A common requirement in GUI applications that use databases is the ability to load, display, and edit data from the database using different widgets. Table, list, and tree widgets are commonly used in GUIs to manage data.

PyQt provides two different kind of widgets for managing data:

  1. Standard widgets include internal containers for storing data.
  2. View widgets don’t maintain internal data containers but use models to access data.

For small GUI applications that manage small databases, you can use the first approach. The second approach is handy when you’re building complex GUI applications that manage large databases.

The second approach takes advantage of PyQt’s Model-View programming. With this approach, you have widgets that represent views such as tables, lists, and trees on one hand and model classes that communicate with your data on the other hand.


Understanding PyQt’s Model-View Architecture

The Model-View-Controller (MVC) design pattern is a general software pattern intended to divide an application’s code into three general layers, each with a different role.

The model takes care of the business logic of the application, the view provides on-screen representations, and the controller connects the model and the view to make the application work.

Qt provides a custom variation of MVC. They call it the Model-View architecture, and it’s available for PyQt as well. The pattern also separates the logic into three components:

  1. Models communicate with and access the data. They also define an interface that’s used by views and delegates to access the data. All models are based on QAbstractItemModel . Some commonly used models include QStandardItemModel , QFileSystemModel , and SQL-related models.

  2. Views are responsible for displaying the data to the user. They also have similar functionality to the controller in the MVC pattern. All views are based on QAbstractItemView . Some commonly used views are QListView , QTableView , and QTreeView .

  3. Delegates paint view items and provide editor widgets for modifying items. They also communicate back with the model if an item has been modified. The base class is QAbstractItemDelegate .

Separating classes into these three components implies that changes on models will be reflected on associated views or widgets automatically, and changes on views or widgets through delegates will update the underlying model automatically.

In addition, you can display the same data in different views without the need for multiple models.



Using Standard Widget Classes

PyQt provides a bunch of standard widgets for displaying and editing data in your GUI applications. These standard widgets provide views such as tables, trees, and lists. They also provide an internal container for storing data and convenient delegates for editing the data. All these features are grouped into a single class.

Here are three of these standard classes:

Standard Class Displays
QListWidget A list of items
QTreeWidget A hierarchical tree of items
QTableWidget A table of items

QTableWidget is arguably the most popular widget when it comes to displaying and editing data. It creates a 2D array of QTableWidgetItem objets. Each item holds an individual value as a string. All these values are displayed and organized in a table of rows and columns.

You can perform at least the following operations on a QTableWidget object:

  • Editing the content of its items using delegate objects
  • Adding new items using .setItem()
  • Setting the number of rows and columns using .setRowCount() and .setColumnCount()
  • Adding vertical and horizontal header labels using setHorizontalHeaderLabels() and .setVerticalHeaderLabels

Here’s a sample application that shows how to use a QTableWidget object to display data in a GUI. The application uses the database you created and populated in previous sections, so if you want to run it, then you need to save the code into the same directory in which you have the contacts.sqlite base de données :

If you double-click any cell of the table, then you’ll be able to edit the content of the cell. However, your changes won’t be saved to your database.

Here’s the code for your application:

 1import sys
 2
 3from PyQt5.QtSql import QSqlDatabase, QSqlQuery
 4from PyQt5.QtWidgets import (
 5    QApplication,
 6    QMainWindow,
 7    QMessageBox,
 8    QTableWidget,
 9    QTableWidgetItem,
10)
11
12class Contacts(QMainWindow):
13    def __init__(self, parent=None):
14        super().__init__(parent)
15        self.setWindowTitle("QTableView Example")
16        self.resize(450, 250)
17        # Set up the view and load the data
18        self.view = QTableWidget()
19        self.view.setColumnCount(4)
20        self.view.setHorizontalHeaderLabels(["ID", "Name", "Job", "Email"])
21        query = QSqlQuery("SELECT id, name, job, email FROM contacts")
22        while query.next():
23            rows = self.view.rowCount()
24            self.view.setRowCount(rows + 1)
25            self.view.setItem(rows, 0, QTableWidgetItem(str(query.value(0))))
26            self.view.setItem(rows, 1, QTableWidgetItem(query.value(1)))
27            self.view.setItem(rows, 2, QTableWidgetItem(query.value(2)))
28            self.view.setItem(rows, 3, QTableWidgetItem(query.value(3)))
29        self.view.resizeColumnsToContents()
30        self.setCentralWidget(self.view)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    if not con.open():
36        QMessageBox.critical(
37            None,
38            "QTableView Example - Error!",
39            "Database Error: %s" % con.lastError().databaseText(),
40        )
41        return False
42    return True
43
44app = QApplication(sys.argv)
45if not createConnection():
46    sys.exit(1)
47win = Contacts()
48win.show()
49sys.exit(app.exec_())

Here’s what’s happening in this example:

  • Lines 18 to 20 create a QTableWidget object, set the number of columns to 4 , and set user-friendly labels for each column’s header.
  • Line 21 creates and executes a SELECT SQL query on your database to get all the data in the contacts table.
  • Line 22 starts a while loop to navigate the records in the query result using .next() .
  • Line 24 increments the number of rows in the table by 1 using .setRowCount() .
  • Lines 25 to 28 add items of data to your table using .setItem() . Note that since the values in the id columns are integer numbers, you need to convert them into strings to be able to store them in a QTableWidgetItem object.

.setItem() takes three arguments:

  1. row holds a zero-based integer that represents the index of a given row in the table.
  2. column holds a zero-based integer that represents the index of a given column in the table.
  3. item holds the QTableWidgetItem object that you need to place at a given cell in the table.

Finally, you call .resizeColumnsToContents() on your view to adjust the size of the columns to their content and provide a better rendering of the data.

Displaying and editing database tables using standard widgets can become a challenging task. That’s because you’ll have two copies of the same data. In other words you’ll have a copy of the data in two locations:

  1. Outside the widget, in your database
  2. Inside the widget, in the widget’s internal containers

You’re responsible for synchronizing both copies of your data manually, which can be an annoying and error-prone operation. Luckily, you can use PyQt’s Model-View architecture to avoid most of these problems, as you’ll see in the following section.



Using View and Model Classes

PyQt’s Model-View classes eliminate the problems of data duplication and synchronization that may occur when you use standard widget classes to build database applications. The Model-View architecture allows you to use several views to display the same data because you can pass one model to many views.

Model classes provide an application programming interface (API) that you can use to manipulate data. View classes provide convenient delegate objects that you can use to edit data in the view directly. To connect a view with a given module, you need to call .setModel() on the view object.

PyQt offers a set of view classes that support the Model-View architecture:

View Class Displays
QListView A list of items that take values directly from a model class
QTreeView A hierarchical tree of items that take values directly from a model class
QTableView A table of items that take values directly from a model class

You can use these view classes along with model classes to create your database applications. This will make your applications more robust, faster to code, and less error-prone.

Here are some of the model classes that PyQt provides for working with SQL databases:

Model Class Description
QSqlQueryModel A read-only data model for SQL queries
QSqlTableModel An editable data model for reading and writing records in a single table
QSqlRelationalTableModel An editable data model for reading and writing records in a relational table

Once you’ve connected one of these models to a physical database table or query, you can use them to populate your views. Views provide delegate objects that allow you to modify the data directly in the view. The model connected to the view will update the data in your database to reflect any change in the view. Note that you don’t have to update the data in the database manually. The model will do that for you.

Here’s an example that shows the basics of how to use a QTableView object and a QSqlTableModel object together to build a database application using PyQt’s Model-View architecture:

To edit the data in a cell of the table, you can double-click the cell. A convenient delegate widget will show in the cell, allowing you to edit its content. Then you can hit Enter to save the changes.

The ability to automatically handle and save changes in the data is one of the more important advantages of using PyQt’s Model-View classes. The Model-View architecture will improve your productivity and reduce the errors that can appear when you have to write data manipulation code by yourself.

Here’s the code to create the application:

 1import sys
 2
 3from PyQt5.QtCore import Qt
 4from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
 5from PyQt5.QtWidgets import (
 6    QApplication,
 7    QMainWindow,
 8    QMessageBox,
 9    QTableView,
10)
11
12class Contacts(QMainWindow):
13    def __init__(self, parent=None):
14        super().__init__(parent)
15        self.setWindowTitle("QTableView Example")
16        self.resize(415, 200)
17        # Set up the model
18        self.model = QSqlTableModel(self)
19        self.model.setTable("contacts")
20        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
21        self.model.setHeaderData(0, Qt.Horizontal, "ID")
22        self.model.setHeaderData(1, Qt.Horizontal, "Name")
23        self.model.setHeaderData(2, Qt.Horizontal, "Job")
24        self.model.setHeaderData(3, Qt.Horizontal, "Email")
25        self.model.select()
26        # Set up the view
27        self.view = QTableView()
28        self.view.setModel(self.model)
29        self.view.resizeColumnsToContents()
30        self.setCentralWidget(self.view)
31
32def createConnection():
33    con = QSqlDatabase.addDatabase("QSQLITE")
34    con.setDatabaseName("contacts.sqlite")
35    if not con.open():
36        QMessageBox.critical(
37            None,
38            "QTableView Example - Error!",
39            "Database Error: %s" % con.lastError().databaseText(),
40        )
41        return False
42    return True
43
44app = QApplication(sys.argv)
45if not createConnection():
46    sys.exit(1)
47win = Contacts()
48win.show()
49sys.exit(app.exec_())

Here’s what’s happening in this code:

  • Line 18 creates an editable QSqlTableModel object.
  • Line 19 connects your model with the contacts table in your database using .setTable() .
  • Line 20 sets the edit strategy of the model to OnFieldChange . This strategy allows the model to automatically update the data in your database if the user modifies any of the data directly in the view.
  • Lines 21 to 24 set some user-friendly labels to the horizontal headers of the model using .setHeaderData() .
  • Line 25 loads the data from your database and populates the model by calling .select() .
  • Line 27 creates the table view object to display the data contained in the model.
  • Line 28 connects the view with the model by calling .setModel() on the view with your data model as an argument.
  • Line 29 calls .resizeColumnsToContents() on the view object to adjust the table to its content.

C'est ça! You now have a fully-functional database application.




Using SQL Databases in PyQt:Best Practices

When it comes to using PyQt’s SQL support effectively, there are some best practices that you might want to use in your applications:

  • Favor PyQt’s SQL support over Python standard library or third-party libraries to take advantage of the natural integration of these classes with the rest of PyQt’s classes and infrastructure, mostly with the Model-View architecture.

  • Use previously prepared dynamic queries with placeholders for parameters and bind values to those parameters using .addBindValue() and .bindValue() . This will help prevent SQL injection attacks.

  • Handle errors that can occur when opening a database connection to avoid unexpected behaviors and application crashes.

  • Close and remove unneeded database connections and queries to free any acquired system resources.

  • Minimize the use of SELECT * queries to avoid problems when retrieving data with .value() .

  • Pass your passwords to .open() instead of to .setPassword() to avoid the risk of compromising your security.

  • Take advantage of PyQt’s Model-View architecture and its integration with PyQt’s SQL support to make your applications more robust.

This list isn’t complete, but it’ll help you make better use of PyQt’s SQL support when developing your database applications.



Conclusion

Using PyQt’s built-in support to work with SQL databases is an important skill for any Python developer who’s creating PyQt GUI applications and needs to connect them to a database. PyQt provides a consistent set of classes for managing SQL databases.

These classes fully integrate with PyQt’s Model-View architecture, allowing you to develop GUI applications that can manage databases in a user-friendly way.

In this tutorial, you’ve learned how to:

  • Use PyQt’s SQL support to connect to a database
  • Execute SQL queries on a database with PyQt
  • Build database applications using PyQt’s Model-View architecture
  • Display and edit data from a database using PyQt widgets

With this knowledge, you can improve your productivity when creating nontrivial database applications and make your GUI applications more robust.