Aller au contenu

Les valeurs dérivées

Un exemplaire prend le rayon de son œuvre. Une ligne de commande prend l’unité et le prix du produit. Un emprunt part de la date du jour. Trois fois la même chose : un champ qui se déduit d’un autre.

from kewos.orm.derived_defaults import DerivedDefault
registry.register_model(
"library_copy",
module="library",
fields=[...],
derived_defaults=(
DerivedDefault(field="shelf", source="title_id.genre"),
),
)

Cinq sources, aucune ligne de code

sourceCe qu’elle vaut
"@current_user"L’utilisateur courant (un champ Link vers l’utilisateur).
"@current_user_record:<champ>"La fiche dont <champ> est l’utilisateur courant. La recherche inverse.
"@today", "@current_month_start", "@current_month_end"La date du jour, le premier ou le dernier jour du mois.
"<champ>"La valeur d’un champ voisin de la même fiche.
"<relation>.<attribut>"Un saut de relation : on charge la cible du Link et on lit son attribut.

C’est une liste fermée, et c’est le sujet. Un champ calculé à la volée par du Python fourni par un utilisateur serait une exécution de code arbitraire (interdiction n°37). Ici, la déclaration est une chaîne, le noyau la résout, et il n’y a rien à exécuter.

À la création : remplir si vide

La dérivation tourne côté serveur, dans la session, donc quelle que soit la porte d’entrée : l’interface, l’API, un import.

POST /api/v1/library_loan {"copy_id":1,"member_id":1,"due_on":"2026-10-01"}
-> {"id":19, "taken_on":"2026-09-06", ...}

taken_on n’était pas dans la demande : il vaut aujourd’hui. Et une valeur fournie n’est jamais écrasée : la règle est « remplir si vide », pas « imposer ».

Dans le formulaire : recalculer au changement

Le pendant interactif est le recalcul. Quand l’utilisateur change un champ dont dépend une dérivation, le formulaire demande au serveur les nouvelles valeurs, avant d’enregistrer :

POST /api/v1/library_copy/recompute
{"values": {"title_id": 1, "barcode": "X-1"}, "changed": "title_id"}
-> {"values": {"shelf": "fiction", "name": "X-1 - fiction"}, ...}
POST /api/v1/library_copy/recompute
{"values": {"title_id": 2, "barcode": "X-1"}, "changed": "title_id"}
-> {"values": {"shelf": "science", "name": "X-1 - science"}, ...}

Le titre 1 est un roman, le titre 2 un essai scientifique : le rayon suit. Notez que le champ calculé name suit aussi, dans le même aller-retour. Le frontend n’a rien calculé : il a envoyé le brouillon et fusionné la réponse.

Un champ obligatoire peut être dérivé

Un champ nullable=False avec une dérivation peut être omis de l’appel : il sera rempli avant l’écriture. C’était faux jusqu’à cette page : la validation du corps de la requête réclamait le champ avant que la session ait son tour, donc la promesse « quelle que soit la porte d’entrée » ne tenait pas pour l’API. Deux modèles du produit étaient dans ce cas (inventory_movement et ses deux emplacements, dérivés de l’opération parente).

Et si la dérivation ne trouve rien, la base refuse la ligne : le serveur répond alors 400 en nommant le champ, pas un 500 et pas une phrase sur les doublons.