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.

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.

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 :

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

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 ?

  1. Le main crée la connexion SQL et démarre le routeur en lui passant l'objet db et l'identifiant "start.
  2. Le routeur démarre avec sa fonction start. Il lance aussitôt la boucle.
  3. 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 fonction choice qui consiste à attendre le choix utilisateur.
  4. 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".
  5. Dans le routeur, ligne 14, identifiant devient donc "artiste:add"
  6. La boucle se répète et cette fois c'est le second test qui sera validé, ligne 15.
  7. Le routeur lance la fonction add du module artiste.
  8. L'utilisateur est invité à entrer des valeurs et l'artiste est créé. La base de donnée renvoie l'id du nouvel item. Disons que cet id est 45.
  9. La fonction add de artiste renvoie donc "artiste:show:45"
  10. Dans routeur, identifiant devient donc égal à "artiste:show:45"
  11. 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 else ligne 17 qui est validé et identifiant repasse à "start", retour au menu principal.
  12. Supposons que l'on choisisse la fin, alors le menu renvoie "fin", dans routeur, identifiant devient égal à "fin" et la boucle s'arrête.
  13. la fonction routeur.start se terminant, on revient dans main qui ferme la connexion SQL.

Bien comprendre ce que l'on gagne

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.