Aller au contenu

Tutoriel : Application complète avec Forge

Ce tutoriel guide le développement d'une petite application Forge de bout en bout.
Il suppose que Forge est déjà installé et que MariaDB est disponible.

Nouveau sur Forge ?

Commencez par Bonjour Forge pour découvrir les
bases avant d'aborder ce tutoriel.


Objectif

À la fin de ce tutoriel, vous aurez :

  • un projet Forge fonctionnel avec plusieurs entités ;
  • deux entités (Ville et Contact) reliées par une relation many_to_one ;
  • des CRUD générés pour chaque entité ;
  • une compréhension claire des fichiers générés et préservés ;
  • un projet validé par les outils de diagnostic Forge.

Ce que tu vas construire

Un carnet de contacts minimal :

  • une entité Ville (nom, code postal) ;
  • une entité Contact (nom, prénom, email, ville) ;
  • une relation : un contact appartient à une ville ;
  • les vues liste, fiche, création, modification et suppression pour chaque entité.

Ce n'est pas une application métier complète. Le but est de montrer Forge :
comment les entités s'organisent, comment les relations se déclarent, et comment
le code généré se distingue du code que vous écrivez.


Le workflow officiel en un coup d'œil

forge make:entity   → crée l'entité JSON (vous éditez les champs)
forge make:relation → déclare les relations dans relations.json
forge build:model   → régénère TOUT depuis le JSON : SQL, _base.py, relations.sql
forge db:init       → crée la base et applique le SQL généré
forge make:crud     → génère contrôleurs, formulaires et vues
forge run           → lance l'application

forge build:model est la commande centrale : elle régénère, pour toutes les
entités, le schéma SQL, l'interface _base.py et le relations.sql, à partir des
fichiers JSON. Les variantes ciblées forge sync:entity <Nom> (une seule entité)
et forge sync:relations (relations seules) servent à régénérer après édition
d'un élément précis ; pour le parcours initial, build:model suffit.


Prérequis

  • Forge installé (forge --version doit répondre) ;
  • Python 3.12 ou plus ;
  • MariaDB installé et démarré (pour forge db:init) ;
  • npm installé (optionnel, pour recompiler le CSS Tailwind).

1. Créer le projet

forge new carnet_contacts
cd carnet_contacts
source .venv/bin/activate

forge new crée la structure du projet, installe les dépendances Python,
génère les certificats SSL de développement, compile le CSS Tailwind si npm
est disponible, et initialise un dépôt Git propre.

Structure créée :

carnet_contacts/
├── app.py                   # point d'entrée
├── env/dev                  # configuration (.env)
├── mvc/
│   ├── routes.py            # déclaration des routes
│   ├── controllers/         # contrôleurs applicatifs
│   ├── models/              # modèles applicatifs
│   ├── forms/               # formulaires
│   ├── views/               # templates Jinja
│   └── entities/            # entités JSON et relations
│       └── relations.json   # relations globales (vide au départ)
├── static/                  # fichiers statiques
└── forge_profile.txt        # profil du projet

2. Vérifier le projet

forge doctor
forge project:check

Ces deux commandes sont complémentaires :

  • forge doctor : diagnostic de l'environnement d'exécution ;
  • forge project:check : cohérence interne du projet Forge (dossiers,
    configuration, entités, routes, templates, modules).

Un projet neuf doit être sain dès la création.


3. Créer l'entité Ville

forge make:entity Ville --no-input

Fichiers créés dans mvc/entities/ville/ (le sous-dossier est en minuscule) :

Fichier Rôle Régénérable ?
ville.json Source de vérité de l'entité Non (source)
ville.sql Schéma SQL généré Oui (build:model)
ville_base.py Interface Python générée Oui (build:model)
ville.py Modèle manuel (vide au départ) Non, préservé

Personnaliser l'entité Ville

Éditez mvc/entities/ville/ville.json au format canonique (clé racine
name, types Forge, pas de champ id ni de type SQL brut) :

{
  "schema_version": "1.0",
  "name": "Ville",
  "table": "villes",
  "fields": [
    {"name": "nom", "type": "string", "max_length": 100, "required": true},
    {"name": "code_postal", "type": "string", "max_length": 10}
  ]
}

Le champ id est implicite

Forge génère la clé primaire id automatiquement : ne la déclarez pas dans
fields. Les types sont ceux de Forge (string, integer, slug,
boolean, date, email…), jamais du SQL brut comme VARCHAR(100).


4. Créer l'entité Contact

forge make:entity Contact --no-input

Fichiers créés dans mvc/entities/contact/, sur le même modèle que ville.

Personnaliser l'entité Contact

Éditez mvc/entities/contact/contact.json :

{
  "schema_version": "1.0",
  "name": "Contact",
  "table": "contacts",
  "fields": [
    {"name": "nom", "type": "string", "max_length": 100, "required": true},
    {"name": "prenom", "type": "string", "max_length": 100, "required": true},
    {"name": "email", "type": "email", "required": true},
    {"name": "ville_id", "type": "integer", "nullable": true}
  ]
}

Le champ ville_id est un entier ordinaire dans le JSON de Contact.
La contrainte de clé étrangère sera déclarée dans relations.json, pas dans
l'entité.


5. Déclarer la relation

La relation entre Contact et Ville est déclarée dans le fichier global
mvc/entities/relations.json.

forge make:relation

forge make:relation est un assistant interactif. Il vous pose les questions
suivantes :

Type de relation : many_to_one
Entité source (from) : Contact
Entité cible (to) : Ville
Clé étrangère (foreign_key) : ville_id

Résultat dans mvc/entities/relations.json :

{
  "$schema": "../../schemas/relations.schema.json",
  "schema_version": "1.0",
  "relations": [
    {
      "type": "many_to_one",
      "from": "Contact",
      "to": "Ville",
      "name": "ville",
      "foreign_key": "ville_id",
      "nullable": true,
      "on_delete": "set_null"
    }
  ]
}

6. Régénérer le modèle

Après avoir édité les entités et déclaré la relation, régénérez tout d'une
seule commande :

forge build:model

forge build:model relit chaque *.json et régénère, pour toutes les entités :

  • le schéma SQL (ville.sql, contact.sql) ;
  • l'interface Python (ville_base.py, contact_base.py) ;
  • le fichier global relations.sql (contraintes ALTER TABLE de la clé étrangère).

Les modèles manuels (ville.py, contact.py) sont préservés : Forge ne les
réécrit jamais. Vérifiez la cohérence à tout moment avec forge check:model.


7. Générer les CRUD

forge make:crud Ville
forge make:crud Contact

Fichiers créés pour Ville

Fichier Modifiable ?
mvc/controllers/ville_controller.py Oui, préservé
mvc/models/ville.py Oui, préservé
mvc/forms/ville_form.py Oui, préservé
mvc/views/ville/list.html Oui, préservé
mvc/views/ville/show.html Oui, préservé
mvc/views/ville/create.html Oui, préservé
mvc/views/ville/edit.html Oui, préservé
mvc/views/ville/delete.html Oui, préservé

Les fichiers de Contact suivent le même schéma (contact_controller.py,
contact_form.py, mvc/views/contact/*.html).

make:crud et les relations

Quand Contact est la source (from) d'une relation many_to_one, forge make:crud
génère automatiquement un champ RelationField pour ville_id dans le formulaire,
et un LEFT JOIN dans la requête de liste pour afficher le nom de la ville.

Fichiers préservés

Ces fichiers ne sont jamais réécrasés par Forge.
Si forge make:crud est relancé sur une entité existante, il refuse
sans l'option --force. Votre code est en sécurité.

Les routes sont ajoutées automatiquement dans mvc/routes.py.


8. Comprendre les fichiers générés

Architecture des fichiers

mvc/entities/
├── contact/
│   ├── contact.json         ← source de vérité (vous éditez ce fichier)
│   ├── contact.sql          ← généré par build:model (régénérable)
│   ├── contact_base.py      ← généré par build:model (régénérable)
│   └── contact.py           ← modèle manuel (préservé)
├── ville/
│   ├── ville.json           ← source de vérité (vous éditez ce fichier)
│   ├── ville.sql            ← généré par build:model (régénérable)
│   ├── ville_base.py        ← généré par build:model (régénérable)
│   └── ville.py             ← modèle manuel (préservé)
├── relations.json           ← source de vérité des relations (vous éditez ce fichier)
└── relations.sql            ← généré par build:model (régénérable)

La règle centrale

JSON  → build:model → SQL + _base.py + relations.sql   (régénérable à tout moment)
JSON  → make:crud   → controller / model / form / views (préservé - jamais réécrasé)

Les fichiers SQL et _base.py sont des projections du JSON : régénérez-les
autant de fois que nécessaire, sans perte. Les fichiers de make:crud et les
modèles manuels sont les vôtres ; Forge n'y touche plus après la création.
Le Contrat de stabilité garantit cette règle.


9. Lancer l'application

Configurer env/dev

# Renseigner les variables MariaDB dans env/dev
DB_ADMIN_LOGIN=root
DB_ADMIN_PWD=<mot_de_passe_root>
DB_APP_LOGIN=carnet_contacts_app
DB_APP_PWD=<mot_de_passe_app>
DB_NAME=carnet_contacts

Initialiser la base

forge db:init

forge db:init crée la base, l'utilisateur applicatif et les tables issues des
fichiers SQL générés (ville.sql, contact.sql) puis applique relations.sql.

Ordre des tables

forge db:init applique d'abord les fichiers SQL des entités, puis
relations.sql. La table villes doit exister avant que la contrainte
de clé étrangère sur contacts soit ajoutée. Cet ordre est géré automatiquement.

Lancer l'application

forge run

L'application démarre sur https://localhost:8000 avec HTTPS de développement.

Les routes disponibles :

/ville           → liste des villes
/ville/new       → créer une ville
/contact         → liste des contacts (avec ville affichée)
/contact/new     → créer un contact (sélection de ville disponible)

10. Contrôles finaux

forge doctor
forge project:check
forge project:audit

Un projet avec deux entités, une relation many_to_one, et les CRUD générés
doit passer les trois contrôles sans fail.

python -m pytest
python -m compileall -q .

Les tests valident les générateurs, le runtime et les entités. La compilation
vérifie l'absence d'erreurs de syntaxe.


11. Limites du tutoriel

Ce tutoriel couvre une application simple. Il ne couvre pas :

Limite Documentation
Auth / connexion utilisateur Auth/User
Rôles et permissions (RBAC) Sécurité et RBAC, RBAC
Relations many_to_many Relations entre entités
Déploiement en production Déploiement
Sécurité en production Sécurité en production

forge make:relation est interactif. Pour les projets sans terminal interactif,
éditez directement mvc/entities/relations.json selon le format documenté
dans Relations entre entités.

forge db:init nécessite un MariaDB local configuré. Sans MariaDB, les
fichiers JSON, SQL, modèles et vues sont générés mais l'application ne peut
pas démarrer.


Récapitulatif des commandes

forge new carnet_contacts          # créer le projet
cd carnet_contacts
source .venv/bin/activate

forge doctor                       # vérifier l'environnement
forge project:check                # cohérence structurelle

forge make:entity Ville --no-input    # créer l'entité Ville, puis éditer ville.json
forge make:entity Contact --no-input  # créer l'entité Contact, puis éditer contact.json

forge make:relation                # déclarer la relation Contact → Ville

forge build:model                  # régénérer SQL + _base.py + relations.sql

forge make:crud Ville              # générer le CRUD Ville
forge make:crud Contact            # générer le CRUD Contact

forge project:check                # vérifier après génération
forge project:audit                # rapport détaillé

forge db:init                      # initialiser la base (MariaDB requis)
forge run                          # lancer l'application

Voir aussi