Aller au contenu

Starter 3 — Carnet de contacts

Accueil Retour

Starter Forge · Niveau 3

Carnet de contacts

Un carnet relationnel simple : `Ville`, `Contact`, relation `many_to_one`, formulaire avec sélection et SQL visible en `LEFT JOIN`.

  • Objectif


    Comprendre une relation Forge V1 sans ORM implicite.

  • Génération


    Disponible avec forge starter:build 3.

  • Modèle


    Deux entités, une relation globale, un SQL de relation visible.

  • Résultat


    Contacts liés optionnellement à une ville.

Génération automatique disponible

Ce starter est générable avec forge starter:build 3, forge starter:build carnet ou forge starter:build carnet-contacts.

Périmètre actuel

La génération automatique crée Ville, Contact et la relation Contact.ville_id -> Ville.id. Elle ne génère pas encore Groupe, ContactGroupe ni de many-to-many explicite.

Prérequis

Prérequis généraux

  • Python 3.11 ou supérieur
  • Git
  • pipx (recommandé) ou environnement virtuel Python
  • MariaDB installé et démarré
  • Accès à un compte administrateur MariaDB (pour forge db:init)
  • Fichier env/dev configuré avec les identifiants MariaDB

Prérequis spécifiques au starter

  • Starters 1 et 2 compris, ou au moins le cycle entité → SQL → CRUD
  • Projet Forge vierge, ou scaffold adoptable (le starter détecte et adopte un relations.json vide)
  • Support des relations Forge : relations.json et relations.sql (générés automatiquement)
  • Utiliser --force uniquement si le starter est déjà partiellement présent dans le projet

Partie 1 — Installer Forge sur une VM Debian vierge

Si Forge est déjà installé et configuré sur votre machine, passez directement à la Partie 2 — Construire l'application starter.

La procédure complète est documentée sur la page Installation sur VM Debian vierge.

Elle couvre en 7 étapes : mise à jour du système, dépendances Python/MariaDB, Node.js optionnel, configuration de pipx, démarrage de MariaDB, vérification de l'accès administrateur et installation de Forge via pipx.

Une fois que forge --version s'affiche correctement, revenez ici pour construire l'application.


Partie 2 — Construire l'application starter

Présentation rapide

Le starter construit une application de carnet de contacts avec :

  • une liste de contacts enrichie par leur ville ;
  • un formulaire Contact avec <select name="ville_id"> ;
  • une page détail Contact ;
  • une liste simple des villes ;
  • un script de seed pour les villes de test ;
  • des requêtes SQL visibles dans le modèle applicatif.
flowchart LR
    A["Contact"] -->|"ville_id nullable"| B["Ville"]
    C["contact_model.py"] -->|"LEFT JOIN visible"| A
    C --> B
    D["relations.json"] -->|"many_to_one"| A

Générer le starter

Depuis un projet Forge vierge ou préparé :

Vérifier d'abord que env/dev contient les identifiants administrateur MariaDB :

DB_ADMIN_LOGIN=root
DB_ADMIN_PWD=<mot_de_passe_root_mariadb>

Compte administrateur MariaDB

La procédure utilise root avec mot de passe. Pour un environnement plus sécurisé, remplacer root par un compte dédié, par exemple forge_admin.

forge doctor
forge starter:build 3 --dry-run
forge starter:build 3 --init-db
python scripts/seed_villes.py

Alias équivalents :

forge starter:build carnet
forge starter:build carnet-contacts

--init-db lance explicitement l'initialisation MariaDB. Sans cette option, la base doit déjà être prête.

--force reconstruit les fichiers du starter 3 et le bloc de routes marqué. Il préserve les fichiers manuels d'entité comme contact.py, ville.py et les __init__.py existants.

Modèle généré

Entités :

Ville
Contact

Relation globale :

Contact.ville_id -> Ville.id

Colonnes SQL importantes :

Entité Champs Python Colonnes SQL
Ville id, nom, code_postal VilleId, Nom, CodePostal
Contact id, nom, prenom, email, telephone, ville_id ContactId, Nom, Prenom, Email, Telephone, VilleId

ville_id est nullable pour permettre :

ON DELETE SET NULL

JSON Et Relations

Le starter injecte deux JSON canoniques :

mvc/entities/ville/ville.json
mvc/entities/contact/contact.json

Il injecte aussi la source canonique relationnelle :

mvc/entities/relations.json

Forge génère ensuite :

mvc/entities/ville/ville.sql
mvc/entities/contact/contact.sql
mvc/entities/relations.sql
Relation générée
{
  "name": "contact_ville",
  "type": "many_to_one",
  "from_entity": "Contact",
  "to_entity": "Ville",
  "from_field": "ville_id",
  "to_field": "id",
  "foreign_key_name": "fk_contact_ville",
  "on_delete": "SET NULL",
  "on_update": "CASCADE"
}

SQL Visible

Le modèle mvc/models/contact_model.py utilise une jointure visible :

SELECT
    contact.ContactId,
    contact.Nom,
    contact.Prenom,
    contact.Email,
    contact.Telephone,
    contact.VilleId,
    ville.Nom AS VilleNom,
    ville.CodePostal AS VilleCodePostal
FROM contact
LEFT JOIN ville ON ville.VilleId = contact.VilleId
ORDER BY contact.Nom, contact.Prenom

Il n'y a pas d'ORM implicite : le modèle applicatif porte explicitement les requêtes nécessaires à l'écran.

Fichiers Créés

mvc/entities/ville/
mvc/entities/contact/
mvc/entities/relations.json
mvc/entities/relations.sql
mvc/controllers/contact_controller.py
mvc/controllers/ville_controller.py
mvc/models/contact_model.py
mvc/models/ville_model.py
mvc/forms/contact_form.py
mvc/views/layouts/app.html
mvc/views/contact/index.html
mvc/views/contact/form.html
mvc/views/contact/show.html
mvc/views/ville/index.html
scripts/seed_villes.py

Routes

GET   /contacts
GET   /contacts/new
POST  /contacts
GET   /contacts/{id}
GET   /contacts/{id}/edit
POST  /contacts/{id}
POST  /contacts/{id}/delete
GET   /villes

Les routes sont injectées entre marqueurs :

# forge-starter:carnet-contacts:start
# ...
# forge-starter:carnet-contacts:end

Elles sont publiques et sans CSRF automatique, car ce starter ne met pas en place d'authentification ni de session utilisateur.

Choix pédagogique — pas une bonne pratique générale

Le starter 3 est volontairement public pour rester centré sur les relations entre entités. Il ne traite pas encore l'authentification. Dans une application réelle, les routes d'écriture (POST /contacts, POST /contacts/{id}, POST /contacts/{id}/delete, POST /villes) devront être protégées et la protection CSRF conservée.

Données De Test

Après génération :

python scripts/seed_villes.py

Le script insère de manière idempotente :

Ville Code postal
Dreux 28100
Chartres 28000
Paris 75000
Lyon 69000
Nantes 44000

Vérification finale

forge check:model
forge routes:list
python app.py

Ouvrir :

https://localhost:8000/contacts
https://localhost:8000/villes

Tester :

  1. créer un contact avec ville ;
  2. afficher son détail ;
  3. modifier sa ville ;
  4. supprimer le contact ;
  5. vérifier la liste des villes.

Suite pédagogique

Le many-to-many explicite avec Groupe et ContactGroupe reste une évolution possible du parcours. Il doit rester modélisé par une entité pivot explicite et deux relations many_to_one, pas par une magie ORM.

Reconstruction

Le fichier court de reconstruction est disponible dans starters/03-carnet-contacts/rebuild.md.


Dépannage rapide

Erreur Cause probable Correction
forge: command not found pipx n'est pas dans le PATH pipx ensurepath puis exec $SHELL -l
No module named venv python3-venv absent sudo apt install python3-venv
mariadb_config not found dépendances MariaDB dev absentes sudo apt install libmariadb-dev pkg-config
Access denied for user 'root'@'localhost' mauvais mot de passe root ou root configuré en unix_socket vérifier le mot de passe, ou tester sudo mariadb
mariadb: command not found client MariaDB absent sudo apt install mariadb-client
erreur de compilation Python outils de build absents sudo apt install build-essential pkg-config libmariadb-dev
erreur certificat HTTPS openssl absent sudo apt install openssl