nsi:terminales:sql_et_python
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Prochaine révision | Révision précédente | ||
| nsi:terminales:sql_et_python [2021/04/13 15:28] – créée goupillwiki | nsi:terminales:sql_et_python [2022/08/29 21:47] (Version actuelle) – goupillwiki | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | < | + | ====== |
| - | # SQL et Python | + | |
| - | ## Requêtes via un langage de programmation | + | ===== Requêtes via un langage de programmation |
| - | Nous avons étudié le langage SQL qui permet de gérer une base de données par l'intermédiaire de requêtes : `CREATE TABLE`, `INSERT`, `UPDATE`, `SELECT`, etc. | + | ==== Utilisation réelle d'une BDD ==== |
| - | Nous avons utilisé un logiciel de gestion nous permettant d' | + | Nous avons étudié le langage SQL qui permet de gérer une base de données par l' |
| + | |||
| + | Nous avons utilisé un logiciel de gestion nous permettant d’écrire | ||
| Mais ceci n'est pas l' | Mais ceci n'est pas l' | ||
| Ligne 12: | Ligne 13: | ||
| Si nous reprenons l' | Si nous reprenons l' | ||
| - | * Une fenêtre - *par exemple une fenêtre web* - met à disposition des boutons permettant à un utilisateur de consulter le contenu de la base ou d'en modifier le contenu. | + | |
| - | + | * Si l' | |
| - | * Si l' | + | * Si l' |
| - | + | ||
| - | | + | |
| - | + | ||
| - | | + | |
| - | + | ||
| - | * Si l' | + | |
| Bref, à aucun moment l' | Bref, à aucun moment l' | ||
| - | Le logiciel (par exemple | + | ==== Dialogue entre le programme |
| - | + | ||
| - | * Sur internet, un usage courant est de passer par le langage PHP. Les reqêtes http venant des clients et arrivant sur le serveur sont pris en charge par un script PHP, exécuté sur le serveur, et PHP intéragit avec un SGBD. | + | |
| - | Dans ce cas, le SGBD est souvent MySQL, MariaDB ou PostgreSQL | + | Le logiciel -- par exemple |
| - | * Un autre usage qui se développe est de faire exécuter un script en Javascript sur le serveur | + | * Sur internet, un usage courant est de passer par le langage [[langages: |
| + | | ||
| + | * Nous allons étudier le cas de Python. | ||
| - | * Nous allons étudier le cas de Python. | + | ===== sqlite3 ===== |
| - | ## sqlite3 | + | En Python, on peut utiliser le module '' |
| - | En Python, on peut utiliser le module `sqlite3` | + | ==== Importer la bibliothèque ==== |
| - | ```python | + | < |
| import sqlite3 | import sqlite3 | ||
| - | ``` | + | </ |
| - | ### Connexion à la base de données | + | ==== Connexion à la base de données |
| On peut ouvrir ou créer un nouveau fichier base de données : | On peut ouvrir ou créer un nouveau fichier base de données : | ||
| - | ```python | + | < |
| - | c = sqlite3.connect('films.db') | + | c = sqlite3.connect(" |
| - | ``` | + | </ |
| - | Ici, la variable | + | Ici, la variable |
| On peut souhaiter travailler sur une base de données temporaire que l'on n' | On peut souhaiter travailler sur une base de données temporaire que l'on n' | ||
| - | ```python | + | < |
| - | c = sqlite3.connect(':memory:') | + | c = sqlite3.connect(":memory:") |
| - | ``` | + | </ |
| - | ### Activation des clés étrangères | + | Dans notre cas, nous reprenons le fichier {{ : |
| + | |||
| + | <code python linenums: | ||
| + | c = sqlite3.connect(" | ||
| + | </ | ||
| + | |||
| + | ==== Activation des clés étrangères | ||
| La vérification de la cohérence des clé étrangères peut être une lourde charge pour le système. On est donc libre de l' | La vérification de la cohérence des clé étrangères peut être une lourde charge pour le système. On est donc libre de l' | ||
| Ligne 64: | Ligne 65: | ||
| Nous souhaitons l' | Nous souhaitons l' | ||
| - | ```python | + | < |
| c.execute(" | c.execute(" | ||
| - | ``` | + | </ |
| - | > Vous pouvez voir que `execute` est une méthode de l' | + | <WRAP tip> Vous pouvez voir que '' |
| - | ### Exécution de requêtes SQL | + | ==== Exécution de requêtes SQL ==== |
| - | L' | + | L' |
| - | ```python | + | < |
| cursor = c.cursor() | cursor = c.cursor() | ||
| - | ``` | + | </ |
| - | Le curseur a lui aussi une méthode | + | Le curseur a lui aussi une méthode |
| - | ```python | + | < |
| cursor.execute(" | cursor.execute(" | ||
| - | ``` | + | </ |
| Écrit tout à la suite, ce n'est pas très lisible. On préfère généralement utiliser plusieurs lignes : | Écrit tout à la suite, ce n'est pas très lisible. On préfère généralement utiliser plusieurs lignes : | ||
| - | ```python | + | < |
| cursor.execute(""" | cursor.execute(""" | ||
| idArtiste INTEGER PRIMARY KEY, | idArtiste INTEGER PRIMARY KEY, | ||
| Ligne 94: | Ligne 95: | ||
| naissance DATE | naissance DATE | ||
| | | ||
| - | ``` | + | </ |
| Les espaces sur la partie gauche n'ont pas d' | Les espaces sur la partie gauche n'ont pas d' | ||
| **Remarques :** | **Remarques :** | ||
| + | * Le '';'' | ||
| + | * Avec '' | ||
| - | * Le `;` final de la requête sert dans le cas où on souhaite faire plusieurs requêtes. Si on ne fait qu'une seule requête, le `;` est inutile. | + | ==== Exemple d'une insertion |
| - | * Avec `sqlite3` on ne peut pas faire en une fois plusieurs requête. Si on a 3 requêtes à faire, il faudra appeler 3 fois la méthodes `cursor.execute`. | + | |
| - | + | ||
| - | ### Exemple d'une insertion | + | |
| Une insertion de base ne pose pas de problème : | Une insertion de base ne pose pas de problème : | ||
| - | ```python | + | < |
| cursor.execute(" | cursor.execute(" | ||
| - | ``` | + | </ |
| Mais cette situation n'est pas réaliste. En général on sera plutôt dans un cas où nous avons obtenu les informations par ailleurs, depuis une interface web par exemple. | Mais cette situation n'est pas réaliste. En général on sera plutôt dans un cas où nous avons obtenu les informations par ailleurs, depuis une interface web par exemple. | ||
| - | ```python | + | < |
| # au moment de faire l' | # au moment de faire l' | ||
| # ces variables avec leur valeur | # ces variables avec leur valeur | ||
| Ligne 120: | Ligne 120: | ||
| biographie = ' | biographie = ' | ||
| naissance = ' | naissance = ' | ||
| - | ``` | + | </ |
| On sait que la requête voulue est : | On sait que la requête voulue est : | ||
| - | ```sql | + | < |
| INSERT INTO ARTISTE(prénom, | INSERT INTO ARTISTE(prénom, | ||
| - | ``` | + | </ |
| + | |||
| + | === Fausse bonne idée === | ||
| On se dit que l'on pourrait en Python fabriquer le texte de la requête de cette façon : | On se dit que l'on pourrait en Python fabriquer le texte de la requête de cette façon : | ||
| - | ```python | + | < |
| texte_requete = " | texte_requete = " | ||
| # puis exécution | # puis exécution | ||
| cursor.execute(texte_requete) | cursor.execute(texte_requete) | ||
| - | ``` | + | </ |
| - | **Il ne faut surtout pas faire cela !!!!!** | + | <WRAP important> |
| - | C'est logique | + | Il serait naturel de faire cela, mais cela pose un gros problème de sécurité appelé **injection SQL** sur lequel on reviendra plus loin. |
| - | **Bonne méthode** | + | === Bonne méthode |
| - | ```python | + | < |
| data = (prénom, nom, biographie, naissance) | data = (prénom, nom, biographie, naissance) | ||
| cursor.execute(" | cursor.execute(" | ||
| - | ``` | + | </ |
| - | 1. Création du tupple contenant les informations à insérer, dans le bon ordre | + | - Création du tupple contenant les informations à insérer, dans le bon ordre |
| - | 2. Lancement de la requête en prévoyant les trous à compléter sous forme de `?` | + | |
| - | Cette méthode ressemble à l' | + | Cette méthode ressemble à l' |
| - | **id de l' | + | === id de l' |
| Quand on insère un nouvel item, on ne précise pas son identifiant, | Quand on insère un nouvel item, on ne précise pas son identifiant, | ||
| Ligne 159: | Ligne 161: | ||
| On peut récupérer cet id directement après l' | On peut récupérer cet id directement après l' | ||
| - | ```python | + | < |
| id_du_dernier_inséré = cursor.lastrowid | id_du_dernier_inséré = cursor.lastrowid | ||
| - | ``` | + | </ |
| - | ### Exemple d'un insertion multiple | + | ==== Exemple d'un insertion multiple |
| On pourrait décider d' | On pourrait décider d' | ||
| - | ```python | + | < |
| - | à_insérer | + | a_inserer |
| (' | (' | ||
| (' | (' | ||
| Ligne 174: | Ligne 176: | ||
| (' | (' | ||
| ] | ] | ||
| - | ``` | + | </ |
| - | Comment les insérer ? On pourrait passer par une boucle | + | Comment les insérer ? On pourrait passer par une boucle |
| - | ```python | + | < |
| - | for item in à_insérer: | + | for item in a_inserer: |
| cursor.execute(" | cursor.execute(" | ||
| - | ``` | + | </ |
| Mais on dispose d'une fonction plus adaptée : | Mais on dispose d'une fonction plus adaptée : | ||
| - | ```python | + | < |
| - | cursor.executemany(" | + | cursor.executemany(" |
| - | ``` | + | </ |
| - | ### Exemple d'une lecture | + | ==== Exemple d'une lecture |
| - | Le plus souvent, on consulte le contenu de la base dans la modifier. On utilise pour cela une requête | + | Le plus souvent, on consulte le contenu de la base dans la modifier. On utilise pour cela une requête |
| - | ```python | + | < |
| cursor.execute(" | cursor.execute(" | ||
| - | ``` | + | </ |
| La requête est alors exécutée mais on n' | La requête est alors exécutée mais on n' | ||
| - | Les résultats de la sélection sont contenus dans `cursor` : | + | Les résultats de la sélection sont contenus dans '' |
| - | ```python | + | < |
| premier = cursor.fetchone() # fetch = chercher. On récupère le premier | premier = cursor.fetchone() # fetch = chercher. On récupère le premier | ||
| suivant = cursor.fetchone() # récupère le suivant | suivant = cursor.fetchone() # récupère le suivant | ||
| suivant = cursor.fetchone() # encore le suivant | suivant = cursor.fetchone() # encore le suivant | ||
| # quand on a tout récupéré, | # quand on a tout récupéré, | ||
| - | ``` | + | </ |
| On peut aussi récupérer les résultats tous d'un coup : | On peut aussi récupérer les résultats tous d'un coup : | ||
| - | ```pyton | + | <code python> |
| resultats = cursor.fetchall() | resultats = cursor.fetchall() | ||
| - | ``` | + | </ |
| - | **Attention :** un résultat aura la forme `(1, ' | + | <WRAP tip> |
| - | ### Fin des modifications | + | ==== Fin des modifications |
| - | Pour que les modifications soit pris en compte dans la base de données, on utilise | + | Pour que les modifications soit prises |
| - | ```python | + | < |
| c.commit() | c.commit() | ||
| - | ``` | + | </ |
| Et pour refermer la connexion : | Et pour refermer la connexion : | ||
| - | ``` | + | <code python> |
| c.close() | c.close() | ||
| - | ``` | + | </ |
| - | + | ||
| - | Attention, la fermeture de la connexion de met pas à jour le fichier base de données et il faut donc penser à faire un `commit` avant la fermeture. | + | |
| - | + | ||
| - | ## Injection SQL | + | |
| - | + | ||
| - | Imaginons que l' | + | |
| - | + | ||
| - | Par exemple, il demande la suppression de l' | + | |
| - | + | ||
| - | Le système doit donc fabriquer la requête avec le paramètre `idArtiste = 1`. Supposons que l'on fasse ceci : | + | |
| - | + | ||
| - | ```python | + | |
| - | # on sait que idArtiste = 1 | + | |
| - | requete = " | + | |
| - | # on a donc : | + | |
| - | # requete = " | + | |
| - | cursor.execute(requete) | + | |
| - | # tout se passe bien | + | |
| - | ``` | + | |
| - | + | ||
| - | Maintenant imaginons qu'un hacker s' | + | |
| - | + | ||
| - | ```python | + | |
| - | idArtiste = '1 OR 1 = 1' | + | |
| - | ``` | + | |
| - | + | ||
| - | Le hacker est maître du client sur lequel il exécute sa requête, il peut donc envoyer la requête qu'il veut. Que se passe-t-il si on fait la même chose qu' | + | |
| - | + | ||
| - | ```python | + | |
| - | # on sait que idArtiste = '1 OR 1 = 1' | + | |
| - | requete = " | + | |
| - | # on a donc : | + | |
| - | # requete = " | + | |
| - | cursor.execute(requete) | + | |
| - | # aie aie aie ! | + | |
| - | ``` | + | |
| - | + | ||
| - | `1 = 1` est toujours vrai, donc la condition du `WHERE` sera toujours vraie et on va donc supprimer tous les artistes de la base !!! | + | |
| - | + | ||
| - | Nous devons donc empêcher que de telles situations se produisent. | + | |
| - | + | ||
| - | Les langages proposent des fonctions, comme le `execute` de `sqlite` avec le `?`, qui se chargent de filtrer tous les cas à problème. C'est pourquoi on ne doit surtout pas essayer de compléter la requête nous-même, on doit utiliser les fonctions fournies. | + | |
| - | + | ||
| - | # Travail | + | |
| - | + | ||
| - | Voilà, vous savez tout. Voilà votre travail. | + | |
| - | + | ||
| - | Créer une interface simple qui utilisera la base de données `films.db`. Vous trouverez un début de fichier Python dans `interface.py`, | + | |
| - | + | ||
| - | Cette interface se comportera comme suit : | + | |
| - | + | ||
| - | * Au démarrage, un menu nous donne le choix de | + | |
| - | + | ||
| - | 1. Ajouter une données | + | |
| - | 2. Afficher les données | + | |
| - | + | ||
| - | L' | + | |
| - | + | ||
| - | * Quand l' | + | |
| - | 1. Ajouter | + | <WRAP important> |
| - | 2. Ajouter un film | + | |
| - | 3. Ajouter un rôle d'un artiste dans un film | + | |
| - | * Dans le cas 1) on demande à l' | + | <WRAP info> |
| - | * Dans le cas 2), on demande à l' | + | |
| - | * Dans le cas 3), on demande le rôle, l'id du film et l'id de l' | + | |
| - | À chaque fois, un message | + | Il n'est pas obligatoire de comprendre ceci. J'indique l'explication pour les plus avancés d' |
| - | * Quand l' | + | Imaginons qu'un site permette aux internautes de créer un compte |
| + | * que c'est une demande de suppression, | ||
| + | * l'id de l'item à supprimer, donc l'id de l' | ||
| - | 1. Liste des artistes | + | Supposons que l' |
| - | 2. Liste des films | + | |
| - | S'il choisit 1) il voit la liste des noms d' | + | Côté serveur, supposons encore |
| - | S'il choisit 2) il voit les infos sur le film avec la liste des noms d' | + | <code python> |
| + | def suppression_artiste(idArtiste): | ||
| + | requete = " | ||
| + | cursor.execute(requete) | ||
| + | </ | ||
| - | Dans les deux cas, on propose à l' | + | Si l' |
| - | Cela fait beaucoup de choses. Allez progressivement. Si vous n'arrivez pas à tout faire, ce n'est pas grave. | + | Supposons maintenant qu'un utilisateur mal intentionné se crée lui aussi un compte. Puis, comme il est maître de sa propre machine, il force l' |
| - | Vous pouvez commencer par : | + | Le programme côté serveur fait la même chose et on obtient |
| - | 1. Permettre l'ajout d'artistes | + | Et encore c'est un cas simple. L'utilisateur pourrait écrire pourrait écrire toute une requête, par exemple en envoyant |
| - | 2. Permettre l'affichage de la liste d'artistes. | + | |
| - | </markdown> | + | Nous devons donc empêcher que de telles situations se produisent. L' |
| + | </WRAP> | ||
nsi/terminales/sql_et_python.1618320488.txt.gz · Dernière modification : de goupillwiki
