2. Ajouter des champs
pipeline_lead appartient à pipeline. Nous allons lui ajouter deux champs sans ouvrir un
seul fichier de ce module.
Le geste
"""Adding two fields to ``pipeline_lead``, without forking it.
``extend_model`` contributes fields to a model another module declared. Thetarget does not need to exist yet: the check is deferred to freeze time, somodule load order stays free.
What this is NOT: an import of ``modules.pipeline``. This module names themodel, never the code behind it. `pipeline` can change its internals, and thiskeeps working."""
def extend_lead(registry: Registry) -> None: """Put "who else is bidding" and "who won" on the opportunity.""" registry.extend_model( "pipeline_lead", module="pipeline_rivals", fields=[ # Who we are up against while the deal is open. Link( name="rivals_competitor_id", target_model="rivals_competitor", nullable=True, on_delete="set_null", ), # Who actually won it. Separate from the field above on purpose: the # competitor we feared is often not the one that took the deal, and # collapsing the two loses exactly the fact worth reviewing. Link( name="rivals_won_by_id", target_model="rivals_competitor", nullable=True, on_delete="set_null", ), ], )Quatre décisions, pas une seule ligne d’accident
Le préfixe. Les champs s’appellent rivals_*, pas competitor_id. Ils vivent dans une
table qui ne nous appartient pas, aux côtés de champs que d’autres extensions peuvent ajouter
demain. Un nom générique, c’est une collision qui attend son heure, et elle se produira chez
un client, pas chez vous.
nullable=True. Non négociable sur un modèle existant. Une colonne obligatoire ajoutée à
une table qui a déjà des lignes ne peut pas être créée : la migration échoue, ou pire, elle
réussit après avoir inventé une valeur par défaut pour des affaires closes il y a deux ans.
on_delete="set_null". Supprimer un concurrent ne doit pas supprimer une affaire. Le lien
disparaît, l’affaire reste. C’est la seule réponse défendable : la donnée qui a de la valeur
ici est l’historique des affaires, pas la fiche du concurrent.
Deux champs, pas un. « Contre qui nous étions » et « qui a gagné » ne sont pas la même information, et ce n’est pas de la coquetterie de modélisation : dans une revue, l’écart entre les deux est précisément ce qu’on cherche. Les fusionner efface la question.
Ce qui arrive en base
Rien à écrire. kewos reconcile voit deux colonnes de plus sur un modèle déjà provisionné et
les ajoute :
main [active]: reconciled (0 migration(s), 2 column(s))done: 1 tenant(s), 0 failedDeux colonnes ajoutées, aucune touchée. C’est ce que le rapprochement de schéma sait faire sans danger ; tout ce qui détruit passe par une migration écrite à la main et confirmée.
L’ordre de chargement ne vous concerne pas
extend_model accepte un modèle qui n’existe pas encore : la vérification est repoussée au
gel du registre. Vous n’avez donc pas à savoir si pipeline a été lu avant vous. Si le modèle
n’existe toujours pas au gel, l’erreur est franche et nomme le modèle manquant, ce qui est le
symptôme d’une dépendance oubliée dans le manifeste.
Les droits suivent
Notre modèle a son propre acl.json, et il réutilise les groupes de pipeline :
{ "rivals_competitor": { "read": ["pipeline_user", "pipeline_manager", "kws_admin"], "create": ["pipeline_manager", "kws_admin"], "write": ["pipeline_manager", "kws_admin"], "unlink": ["kws_admin"] }}Nommer pipeline_manager n’est pas un import : c’est un code, une chaîne de caractères
stable, exactement comme un nom de modèle. Personne n’a à recevoir un nouveau groupe pour voir
les concurrents ; qui gère le portefeuille les gère.
Et ensuite
Les champs existent, l’API les renvoie déjà. Ils ne sont sur aucun écran : Compléter un écran.