Files
training.python.beginner/documentation/09-sqlite.md
2025-07-04 19:26:39 +02:00

271 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Le SQL avec SQLite3
author: Steve Kossouho
---
# Premiers pas en SQL avec SQLite3
----
## DBAPI c'est quoi ?
Python propose une API unifiée standardisée pour accéder à des bases de données SQL (_Structured Query Language_), nommée DBAPI.
Les objets et méthodes ont toujours les mêmes noms et le même usage.
Il existe plusieurs bibliothèques compatibles pour accéder à des systèmes différents.
La bibliothèque standard de Python propose une API compatible pour **SQLite3**.
[**SQLite3**](https://www.sqlite.org/index.html) est un moteur de bases de données relationnelles au format fichier, facile à mettre en place, et adapté pour des logiciels de bureau (ex. Firefox l'utilise). Il n'est pas adapté pour être utilisé dans une application web en production, car il ne gère pas les rôles et droits, ni ne tolère les multiples connexions (en écriture notamment).
----
## Manipuler une base avec DBAPI
Pour travailler sur notre première base de données, nous allons découper l'exemple en quatre étapes.
----
### Étape 1 : Se connecter à la base
```python {.numberLines}
import sqlite3
connection = sqlite3.connect("intro.sqlite3", isolation_level=None)
connection.close()
```
Crée un nouveau fichier de base de données SQLite3, pour le moment vide.
----
### Étape 2 : Créer un schéma de base
On brode sur l'exemple précédent pour créer une nouvelle table.
Cette table ne possède pas de contrainte particulière et autorise à enregistrer des doublons
(mêmes prénom, nom et âge).
```python {.numberLines}
import sqlite3
connection = sqlite3.connect("intro.sqlite3", isolation_level=None)
cursor = connection.cursor() # cet objet est utilisé pour envoyer des requêtes
cursor.execute("""
CREATE TABLE IF NOT EXISTS person (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
nom varchar(30) NOT NULL,
prenom varchar(20) NOT NULL,
age int unsigned NOT NULL
)""")
connection.close()
```
----
### Étape 3 : Ajouter des données
On ajoute ensuite quelques données dans notre nouvelle table :
```python {.numberLines}
import sqlite3
# Requête d'insertion utilisant la notation QMark (protection contre injections)
PERSON_INSERT_QUERY = "INSERT OR IGNORE INTO person (id, nom, prenom, age) VALUES (NULL, ?, ?, ?)"
connection = sqlite3.connect("intro.sqlite3", isolation_level=None)
cursor = connection.cursor()
...
cursor.execute(PERSON_INSERT_QUERY, ("Bouquet", "Carole", 63))
cursor.execute(PERSON_INSERT_QUERY, ("Connery", "Sean", 80))
cursor.execute(PERSON_INSERT_QUERY, ("Trintignant", "Jean-Louis", 53))
connection.close()
```
----
### Étape 4 : Requêter et parcourir des résultats
Comme pour les fichiers texte dans lesquels nous pouvons naviguer dans les lignes via une boucle `for`{.python},
exécuter une commande SQL destinée à renvoyer des résultats autorise à parcourir l'objet de type `Cursor`{.python} via une boucle `for`{.python} pour récupérer des résultats :
```python {.numberLines}
import sqlite3
connection = sqlite3.connect("intro.sqlite3", isolation_level=None)
cursor = connection.cursor()
...
# Exécuter une requête pour retrouver des données
cursor.execute("SELECT * FROM person WHERE age > 50")
for result in cursor: # s'arrête automatiquement quand il n'y a plus de résultat
print(result) # result est toujours un tuple correspondant à un résultat
connection.close()
```
----
### Bonus : Récupérer des résultats sous forme de dictionnaires
Il est possible de configurer l'objet de connexion SQLite3 pour récupérer des objets de type `sqlite3.Row`{.python} :
```python {.numberLines}
import sqlite3
connection = sqlite3.connect("demo.sqlite", isolation_level=None)
# Configure la connexion pour que les requêtes renvoient des objets Row
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
...
cursor.execute("SELECT * FROM person WHERE age BETWEEN 20 AND 40")
for result in cursor: # type: sqlite3.Row
print(result, dict(result))
connection.close()
```
Les objets de type `sqlite3.Row`{.python} sont utilisables comme des dictionnaires, et permettent ainsi d'extraire des résultats en utilisant les noms des colonnes. Ils sont aussi convertibles directement en dictionnaires.
----
## Hors-programme : Requêtes universelles avec les ORMs
C'est bien d'envoyer du SQL à une base de données, mais le problème du standard SQL, c'est que
tous les systèmes de gestion de bases de données ne gèrent pas toujours le SQL de la même façon;
certains proposent leur propre syntaxe ou ne prennent pas en charge une partie du standard, etc…
Sur un projet de taille moyenne, et même de taille modeste, si vous écrivez tout votre code SQL à la main,
basculer d'un _SGBDR_ à un autre (par exemple pour un passage en production) nécessitera
très probablement de retoucher ou d'adapter vos requêtes.
----
Ce problème est, entre autres questionnements, à l'origine de l'existence des ORMs (`Object Relational Mapper`).
Ce sont des bibliothèques qui offrent une abstraction de la base de données et la remplacent par
l'écriture de code, non plus en SQL, mais dans votre langage **orienté objet** préféré,
pour représenter des schémas et manipuler vos données.
----
Les ORMs font généralement usage des concepts suivants :
| Objet | Correspond à... |
|--------------------|:----------------------------------------|
| Classe Modèle | Table de la base de données |
| Attribut de classe | Colonne d'une table |
| Instance de Modèle | Ligne d'une table de la base de données |
En plus de cela, le même code peut être utilisé pour basculer d'un SGBD à un autre (s'il est pris
en charge par l'ORM).
----
### Exemples d'ORMs Python connus :
- [SQLAlchemy](https://www.sqlalchemy.org/)
- [Peewee](http://docs.peewee-orm.com/en/latest/)
- [Pony ORM](https://docs.ponyorm.org/toc.html)
- [Django ORM](https://docs.djangoproject.com/fr/3.2/topics/db/queries/) (framework web)
----
## Bonus : Petite explication sur `isolation_level`
Dans notre premier slide contenant du code, nous avons défini un argument facultatif à la fonction `connect`, nommé
`isolation_level`. Celui-ci permet de définir le [niveau d'isolation](https://en.wikipedia.org/wiki/Isolation_(database_systems)) des commandes SQL que l'on envoie.
Si l'on passe la valeur `None`{.python} à cet argument, l'auto-commit est activé, ce qui signifie que nous n'avons plus besoin
d'appeler manuellement `commit()`{.python} lorsqu'on souhaite persister nos modifications. Ceci est normalement le comportement
par défaut pour toute bibliothèque DBAPI, mais pour une raison inconnue, cela n'est pas le cas pour `sqlite`. Le bug sera corrigé
dans Python 3.12.
----
En temps normal, si nous n'avions pas précisé ce paramètre :
- Ouvrir la connexion (sans autocommit)
- `connection.execute()`{.python} → ouvre une transaction
- `connection.execute()`{.python}
- `connection.execute()`{.python}
- Aucune modification n'est persistée tant qu'on n'appelle pas `connection.commit()`{.python}
Le fait de devoir `cnx.commit()`{.python} par défaut n'est pas spécialement intuitif, notamment
lorsqu'on débute en bases de données...
----
Grâce à notre paramètre `isolation_level`, le comportement est plus intuitif par défaut :
- Ouvrir la connexion (avec autocommit)
- `connection.execute()`{.python} → modification immédiate
- `connection.execute()`{.python} → modification immédiate
- `connection.execute("BEGIN")`{.python} → Ouverture explicite d'une transaction
- `connection.execute()`{.python} → modification non persistée
- `connection.execute("COMMIT")`{.python} → ferme la transaction et applique les modifications si
possible
----
## Bibliothèques Python compatibles DB API 2.0 :
- MySQL : `pip install mysql-connector-python`{.shell} ([doc](https://dev.mysql.com/doc/connector-python/en/))
- PostgreSQL : `pip install psycopg`{.shell} ([doc](https://www.psycopg.org/docs/))
- SQL Server : `pip install pymssql`{.shell} ([doc](https://pymssql.readthedocs.io/en/stable/))
- Oracle : `pip install cx_Oracle`{.shell} ([doc](https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html))
----
## Navigateur de bases de données intégré à Pycharm
Vous pouvez naviguer dans vos bases de données graphiquement dans PyCharm
grâce à un plugin de l'IDE, nommé *Database Navigator*.
Pour l'installer :
1. Cliquez sur `File`, `Settings`, puis la section `Plugins`.
2. Cliquez sur l'onglet `Marketplace`
3. Juste en-dessous de l'onglet `Marketplace`, dans le champ de recherche, saisissez "Database"
4. Vous devriez voir `Database Navigator` apparaître dans les résultats.
5. En regard de ce résultat, cliquez sur `Install`.
6. Si l'installation fonctionne, cliquez en regard du résultat sur `Restart IDE` pour redémarrer PyCharm.
----
### Configurer DB Navigator
Si vous venez d'installer le plugin, vous devez révéler le panneau de l'extension.
Le plus simple est d'utiliser le menu de PyCharm pour le trouver :
![Afficher DB Browser](assets/images/database-show-db-browser.png)
----
Vous pouvez déplacer le panneau avec un clic droit sur sa barre de titre, puis en cliquant sur `Move`.
Cela vous permettra de ne pas masquer le panneau de projet lorsque vous travaillez sur votre base de données.
----
### Configurer une base de données
Dans le panneau DB Browser, cliquez sur l'icône d'ajout pour créer une configuration, puis choisissez SQLite.
1. Dans l'onglet `Connections` et le sous-onglet `Database`,
2. Choisissez un nom à votre connexion. Il vous servira juste visuellement
3. Dans le tableau de l'option `Database files`, cliquez sur `sqlite.db`, puis cliquez sur le bouton qui apparaît au bout de la ligne de texte, afin de choisir le bon fichier de base de données SQLite à surveiller.
4. Dans l'onglet `Connections`, cliquez le sous-onglet `Details`, puis :
5. Réduisez toutes les options `Idle time...` à `1`
6. Décochez `Connect automatically`
7. Terminez la configuration en cliquant le bouton `OK`.
----
Vous pouvez ensuite voir dans le panneau `DB Browser` que votre connexion est prête, dans une arborescence.
Cliquez sur le nœud principal de l'arborescence, puis cliquez sur `Connect`
(il faut se connecter à la base pour pouvoir en explorer le contenu)
----
![Onglet Database](assets/images/database-configure-connection.png)
----
![Onglet Details](assets/images/database-configure-connection-settings.png)