3. Les écrans
This content is not available in your language yet.
Un écran est un fichier JSON dans views/, nommé <modèle>.<genre>.json. Pas un gabarit, pas
un composant, pas de CSS.
Huit genres se déclarent. Les voici tous, sur le module de bibliothèque.
La liste
{ "model": "library_title", "kind": "list", "fields": ["name", "author", "genre", "published_year", "isbn"]}
Vous avez déclaré cinq colonnes. La pagination, le tri, le filtre, la sélection multiple, le sélecteur de modèle, le bouton « New » et le sélecteur de genre de vue viennent du moteur.
Le formulaire
{ "model": "library_title", "kind": "form", "sections": [ { "title": "section.work", "fields": ["name", "author", "genre", "published_year"] }, { "title": "section.reference", "fields": ["isbn", "summary"] } ]}
Chaque champ a pris le contrôle qui correspond à son type : liste déroulante pour la sélection, champ numérique pour l’entier, zone de texte pour le résumé. Vous n’avez rien choisi.
layout accepte cards, accordion ou tabs si vous voulez une autre disposition des
sections.
Le kanban
{ "model": "library_loan", "kind": "kanban", "groups_by": "state", "fields": ["copy_id", "member_id", "due_on"]}
groups_by prend un champ de sélection ou un many2one : ses valeurs deviennent les
colonnes. Les cartes se déplacent d’une colonne à l’autre, et le déplacement écrit le champ.
Remarquez que les en-têtes portent les libellés traduits (« Out », « Returned », « Lost ») et leur compte, et que les cartes affichent le nom calculé de l’exemplaire plutôt qu’un identifiant.
Le même genre, sur le catalogue, groupé par genre littéraire :

Le calendrier
{ "model": "library_loan", "kind": "calendar", "date_field": "due_on", "fields": ["copy_id", "member_id"]}
Un seul champ obligatoire : la date qui place l’enregistrement. Ici, la date de retour prévue, ce qui donne au bibliothécaire la vue qui l’intéresse.
La frise
{ "model": "library_loan", "kind": "timeline", "start_field": "taken_on", "end_field": "due_on", "label_field": "member_id", "group_by": "state", "default_zoom": "week"}
Chaque enregistrement devient une barre entre deux dates. group_by fabrique les lignes,
default_zoom choisit l’échelle initiale parmi jour, semaine, mois, trimestre et année.
La frise accepte aussi progress_field (une barre de progression dans la barre),
dependency_field (des flèches entre les barres) et milestone_field (des jalons).
Le tableau croisé
{ "model": "library_loan", "kind": "pivot", "row_field": "state", "col_field": "member_id", "agg": "count", "chart": "bar"}
row_field seul suffit ; col_field ajoute la seconde dimension. agg accepte count, sum,
avg, min et max, et measure_field désigne alors le champ mesuré. chart affiche en plus
un graphique.
Le tableau de bord
{ "model": "library_title", "kind": "dashboard", "title": "library_title.dashboard.title", "kpis": [ { "key": "titles", "label": "library_title.dashboard.titles", "op": "count" } ], "charts": [ { "key": "by_genre", "label": "library_title.dashboard.by_genre", "chart_type": "column", "groupby": "genre", "op": "count" } ]}Des indicateurs et des graphiques, sur un seul modèle. op accepte count, sum et avg ;
chart_type accepte column, bar, line, area, pie et donut.
Notez que label est une clé de traduction, ici comme partout.
La charge
{ "model": "library_loan", "kind": "workload", "group_by": "member_id", "start_field": "taken_on", "end_field": "due_on", "measure_field": "fine_amount", "capacity_per_period": 40, "default_zoom": "week"}Une frise qui cumule : elle répond à « qui est surchargé sur quelle période ».
capacity_per_period trace le seuil au-delà duquel la période est en dépassement.
La galerie
Celle-ci ne se déclare pas : elle apparaît dès que le modèle a une image.
registry.register_model( "library_title", module="library", fields=[ ..., FileField( name="cover", allowed_extensions=("png", "jpg", "jpeg", "webp", "svg"), max_size_bytes=5 * 1024 * 1024, ), ], # This one line is what makes the gallery view exist. image_field="cover",)Pas de fichier de vue, pas de configuration : image_field désigne le champ qui porte l’image
du modèle, et le moteur ajoute la galerie à la liste des vues offertes.

La même image sert aussi d’avatar ailleurs : dans un sélecteur de relation, dans un en-tête d’enregistrement, partout où le produit montre un ouvrage.
Le tableau des huit
| Genre | Obligatoire | Optionnel |
|---|---|---|
list | fields | name |
form | fields | sections, layout |
kanban | groups_by, fields | name |
calendar | date_field | fields |
timeline | start_field, end_field | label_field, group_by, progress_field, dependency_field, milestone_field, default_zoom |
workload | group_by, start_field, end_field | measure_field, capacity_per_period, default_zoom |
pivot | row_field | col_field, measure_field, agg, chart |
dashboard | kpis, charts | title |
Deux genres de plus n’ont pas de fichier de déclaration : le moteur les propose quand le modèle s’y prête.
| Genre | Apparaît quand |
|---|---|
gallery | Le modèle déclare un image_field. |
alphabetic | Le modèle a un champ texte servant de titre. |
Ce que le serveur envoie vraiment
C’est ici que le modèle prend tout son sens.
curl -H "Authorization: Bearer $TOKEN" \ "http://127.0.0.1:8080/api/v1/views/library_loan/effective?kind=list"{"model":"library_loan","kind":"list","body":{"columns":[ {"name":"copy_id","label":"Copy id","label_i18n_key":"library_loan.copy_id", "type":"many2one","required":true,"target_model":"library_copy","origin":"system"}, {"name":"state","label":"State","label_i18n_key":"library_loan.state", "type":"selection","options":[ {"value":"out","label":"Out","i18n_key":"library_loan.state.out"}]}]}}Le serveur ne renvoie pas seulement des données : il renvoie le type de chaque colonne, son libellé, sa clé de traduction, sa cible de relation et ses options. L’administration n’a besoin de connaître aucun nom de champ à l’avance.
C’est pour cette raison qu’un module apparaît sans toucher au frontend, et c’est aussi pour cette raison qu’un module ne peut pas imposer sa propre mise en page : il décrit, le moteur rend.
Et ensuite
Le rendre visible. Parce qu’à ce stade, votre module a quatorze écrans que personne ne trouvera.