Table des matières
Utilisation de contrôleurs
Dans le cadre du projet SQL, nous avons vu que l'application pouvait prendre des proportions telles que le mélange entre ce qui relevait de texte affichés, et le fonctionnement de l'appli, devenait problématique.
Je vais vous donner ici un guide d'une démarche possible pour séparer les choses.
Notez que le choix de faire une interface en mode texte est avantageux dans le sens où on se limite à des print et des input. D'un autre côté, quand on dispose d'une interface plus complexe, comme une page web, on a accès à des outils qui font déjà une partie du travail. Je pense par exemple au Combos, c'est bouton permettant de choisir dans une liste.
Pour mes exemples, je poursuis avec l'exemple de la base de données avec films et artistes.
Que veut-on afficher ?
C'est la première question à se poser.
- Des menus dont les items sont fixés d'avance.
- Des questions. On peut par exemple demander de choisir parmi les items d'une liste ou encore une valeur à entrer dans la base, par exemple le nom d'un artiste.
- Des informations provenant de la base. Ces informations viennent sous deux formes :
- il peut s'agir de listes, par exemple la liste des titres de films.
- il peut s'agit des infos sur un item, par exemple la fiche détaillée d'un film.
Tout ces éléments peuvent se mélanger. Par exemple, si j'affiche un artiste, je peux prévoir de faire suivre la fiche artiste d'un menu me permettant d'afficher tel films auquel il a participé.
Il y a aussi des comportements à prévoir.
- Le choix d'un item de menu, provoque l'affichage d'un nouveau contenu
- La saisie d'une suite d'informations amène à une action sur la base de données, suivie par l'affichage d'un nouveau contenu.
On ne pourra pas éviter de produire tout un tas d'items, objets, fonctions qui se ressemblent. La structure utilisée pour créer un film va ressembler à celle permettant d'ajouter un artiste. On peut réfléchir beaucoup pour ne pas écrire deux fois la même chose, mais il y aura toujours des petites particularités que l'un a mais pas l'autre et qui rendront la tâche difficile.
Exemple de l'artiste
Pour rendre le code plus simple et mieux organisé, on essaie d'organiser chaque partie du programme indépendamment des autres. Nous allons créer un module rien que pour le cas artiste. Ce module pourra gérer l'ajout d'un artiste et l'affichage d'une fiche artiste.
Le module artiste ne doit pas dépendre des modules concernant les films par exemple.
#module artiste
import sql
def add():
"""ajout d'un artiste dans la base"""
print("Ajouter un artiste.")
nom = input("Donnez son nom :")
prenom = input("Donnez son prénom :")
naissance = input("Donnez sa date de naissance :")
bio = input("Donnez sa biographie :")
sql.insert_artiste(prenom, nom, naissance, bio)
def display(id:int):
"""affichage de l'artiste d'identifiant id"""
# pas fait
# pourrait afficher la liste des films auxquels l'artiste a participé
pass
Quelques remarques :
- Ce module est très court. On pourrait se dire que c'est inutile de faire des fichiers si petits. On va se retrouver avec beaucoup de petits fichiers. Eh bien oui, c'est souvent ainsi. Dans le cas des projets web, au moment du développement, on a des dizaines de petits fichiers. Au moment de la publication du site sur internet, il y a moyen de tout rassembler automatiquement pour ne plus faire qu'un gros fichier plus facile à télécharger pour le navigateur.
- Le contenu texte est encore mélangé avec le programme. Ce n'est pas évident à éviter ici. Dans le cas d'une page web ce serait plus simple : on créerait un texte html qui contiendrait à la fois le texte des question et aussi les blocs
<input>. - cela suppose bien sûr d'avoir créer la fonction
insert_artistedans le modulesql.
si au contraire vous avez opté pour un objet SQL, alors il faut fournir cet objet :#module artiste def add(db): """db: objet SQL ajout d'un artiste dans la base """ print("Ajouter un artiste.") nom = input("Donnez son nom :") prenom = input("Donnez son prénom :") naissance = input("Donnez sa date de naissance :") bio = input("Donnez sa biographie :") db.insert_artiste(prenom, nom, naissance, bio) def display(db, id:int): pass
Mais il manque encore un élément : Que fait-on après, une fois que l'insertion a réussi ? On veut peut-être afficher le contenu de l'artiste que l'on vient de créer. Mais la fonction n'est pas encore créée… Et dans la fonction display, si on affiche les films de l'artiste, il faudrait appeler un module concernant les films… Mais on ne veut pas que les modules soient inter-dépendants.
La solution à ce problème consiste à utiliser un module de routage.
Routeur
Le routeur sera le chef d'orchestre. Il importera tous les modules correspondant aux morceaux de l'application et il sera chargé de les lancés les uns après les autres.
Le routeur fonctionne travaille sur un identifiant, par exemple une chaîne de texte qui lui dit quoi faire. Par exemple, on convient que
"start"lui demande d'afficher le menu principal,"fin"lui demande d'arrêter,"artiste:add"lui demande d'afficher l'ajout d'artiste.
Chaque fois qu'il lance un module, le routeur attend la réponse qui prend la forme d'un nouvel identifiant lui permettant de poursuivre.
Le mieux est encore de poursuivre l'exemple.
Pour la suite, je suppose que l'on a une classe SQL
# main import sql import routeur db = SQL() routeur.start(db, "start") db.close()
# module principal
# j'ajoute un menu principal très simple pour faciliter la compréhension du reste
def display():
print("1. Ajouter artiste")
print("2. Autre chose")
print("3. fin")
def choice():
c = input("Entrez votre choix :")
if c == '1':
return "artiste:add" # notez l'identifiant renvoyé
if c == '3':
return "fin"
#module artiste
def add(db):
"""db: objet SQL
ajout d'un artiste dans la base
"""
print("Ajouter un artiste.")
nom = input("Donnez son nom :")
prenom = input("Donnez son prénom :")
naissance = input("Donnez sa date de naissance :")
bio = input("Donnez sa biographie :")
id = db.insert_artiste(prenom, nom, naissance, bio)
return f"artiste:show:{id}" # notez l'identifiant renvoyé
def show(db, id:int):
pass
# module routeur
import principal
import artiste
def start(db, identifiant:str):
"""
db: objet SQL
identifiant: identifiant du menu à afficher en premier
"""
while identifiant != "fin":
if identifiant == "start":
principal.display()
identifiant = principal.choix()
elif identifiant == "artiste:add":
identifiant = artiste.add(db)
else:
print("identifiant inconnu !")
identifiant = "start"
Que se passe-t-il ?
- Le
maincrée la connexion SQL et démarre le routeur en lui passant l'objetdbet l'identifiant"start. - Le routeur démarre avec sa fonction
start. Il lance aussitôt la boucle. - Le premier test est validé, le routeur lance l'affichage dans le module
principal, ce qui provoque l'affichage de ce menu, puis exécute la fonctionchoicequi consiste à attendre le choix utilisateur. - La fonction
choice, en fonction du numéro entré par l'utilisateur, renvoie un identifiant. Supposons que l'utilisateur a entré1, l'identifiant renvoyé est donc"artiste:add". - Dans le routeur, ligne 14, identifiant devient donc
"artiste:add" - La boucle se répète et cette fois c'est le second test qui sera validé, ligne 15.
- Le routeur lance la fonction
adddu moduleartiste. - L'utilisateur est invité à entrer des valeurs et l'artiste est créé. La base de donnée renvoie l
'iddu nouvel item. Disons que cetidest45. - La fonction
adddeartisterenvoie donc"artiste:show:45" - Dans routeur,
identifiantdevient donc égal à"artiste:show:45" - Puisque nous n'avons pas encore réalisé l'affichage d'un utilisateur, nous avons pas encore ajouté ce genre d'identifiant dans le routeur et donc il n'est pas reconnu. C'est donc le
elseligne 17 qui est validé etidentifiantrepasse à"start", retour au menu principal. - Supposons que l'on choisisse la fin, alors le menu renvoie
"fin", dansrouteur,identifiantdevient égal à"fin"et la boucle s'arrête. - la fonction
routeur.startse terminant, on revient dansmainqui ferme la connexion SQL.
Bien comprendre ce que l'on gagne
- Le programme est fractionné en unités plus facile à comprendre.
- On n'a pas besoin de tout faire d'un seul coup.
- Notre fonction
artiste.addse contente de renvoyer un identifiant. Elle n'a pas à connaître les modules qui feront le nécessaire. Seulrouteura besoin de connaître tous les modules. - Quand on en a le temps, on peut développer la fonction
showdeartistepermettant d'afficher un artiste et alors on peut ajouter dansrouteurla route qui mène à cette fonction, c'est à dire qu'on ajoute un test permettant de reconnaître"artiste:show:45“et déclencher l'affichage.
Plus d'infos sur l'affichage de l'artiste
Vous avez dû voir que l'identifiant dans ce cas est différent : "artiste:show:45"
Dans routeur, on ne va pas créer un test pour tous les id possibles : "artiste:show:1", "artiste:show:2", "artiste:show:3", …
On doit donc être capable de reconnaître que "artiste:show:45" commence par "artiste:show" puis extraire la valeur 45. Il s'agit d'une simple fonction de gestion de texte.
cela donnerait quelque chose comme :
# module routeur
import principal
import artiste
def start(db, identifiant:str):
"""
db: objet SQL
identifiant: identifiant du menu à afficher en premier
"""
while identifiant != "fin":
if identifiant == "start":
principal.display()
identifiant = menu_principal.choix()
elif identifiant == "artiste:add":
identifiant = artiste.add(db)
elif identifiant commence par "artiste:affiche":
id = extraire id de identifiant
identifiant = artiste.show(db, id)
else:
print("identifiant inconnu !")
identifiant = "start"
Séparer le texte du programme
Ce serait préférable mais ce serait trop compliqué ici. Je vous donne pour information l'idée générale.
Le principe est d'utiliser un fichier texte qui contient l'affichage voulu. Dans le cas d'un site web, il peut s'agir de toute une page html ce qui donne beaucoup de possibilités. Dans le cas d'une page html on pourra d'ailleurs inclure les questions sous forme d'input divers. Avec notre mode texte nous sommes plus limités.
Exemple d'un menu
Par exemple on aurait un fichier pour le menu principal :
1. Ajouter artiste 2. Autre chose 3. fin Entrez votre choix
Le module de menu principal n'aurait qu'à ouvrir ce fichier et en afficher le contenu. Ainsi le texte du menu serait séparé et on pourrait en confier la rédaction à quelqu'un qui ne maîtrise pas la programmation.
Exemple d'un panneau de saisie
Pour l'ajout d'un artiste, on pourrait imaginer quelque chose comme
nom = Donnez son nom : prenom = Donnez son prénom : naissance = Donnez sa date de naissance : bio = Donnez sa biographie :
Exemple d'un affichage de contenu
Et le programme Python devrait charger le texte et l'analyser pour repérer les = et transformer ces lignes en une série de input.
Enfin pour l'affichage d'une fiche, on pourrait utiliser un fichier comme :
Fiche de {prenom} {nom}
Né(e) le {naissance}
biographie : {bio}
Et le programme Python devrait charger ce texte et boucher les champ avec les valeurs voulues.
