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 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.12 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

Profil recommandé

Ce starter est le starter relationnel officiel de Forge. Il correspond au profil standard. Voir Profils de projet pour choisir le bon profil au moment de forge new.

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