Aller au contenu

Imports et exports

Un module n’écrit pas son propre import. Le noyau en fournit un, et il impose une chose que personne n’impose spontanément à soi-même : un essai à blanc avant toute écriture.

Sortir

Toute liste s’exporte, sans qu’aucun module n’ait rien à déclarer :

Fenêtre de terminal
curl -H "Authorization: Bearer $TOKEN" \
"/api/v1/library_title/export?format=csv"
id,name,author,isbn,published_year,genre,summary,cover
1,Dune,Frank Herbert,9780441013593,1965,fiction,"A desert planet, a spice, and an empire...",8

Quatre formats : csv, json, jsonl, xml. L’export passe par la session filtrée : un lecteur n’exporte que ce qu’il a le droit de lire, et une ligne interdite n’est pas « masquée », elle n’existe pas dans le fichier.

Les colonnes de tenue de livres (version, created_by_id, deleted_at…) ne sortent pas. Ce n’est pas de la coquetterie : l’import que cet export sert à nourrir les refuse, donc les livrer casserait la boucle. id reste, parce que c’est par lui qu’une ligne modifiée retrouve sa fiche.

Pour choisir, et fixer l’ordre :

Fenêtre de terminal
"/api/v1/library_title/export?format=csv&fields=isbn,name,genre"
isbn,name,genre
9780441013593,Dune,fiction

Un nom inconnu est refusé plutôt qu’ignoré :

422 {"detail":"unknown export field(s): titel"}

Une faute de frappe qui exporte une colonne vide ressemble exactement à un modèle qui ne contient rien.

Rentrer

L’import est un plan qu’on approuve, pas un bouton. Quatre états, et on ne saute aucun :

analysé → approuvé → (essai à blanc) → exécuté

1. Déposer. Le fichier monte, le noyau le lit et propose une correspondance colonnes → champs. mode=ai la fait deviner ; mode=manual la laisse vide, à vous de la remplir.

Fenêtre de terminal
curl -X POST /api/v1/admin/imports \
-H "Authorization: Bearer $TOKEN" \
-F "file=@new_titles.csv" -F "mode=manual"

2. Corriger la correspondance, par PATCH sur le plan : chaque colonne source pointe vers un champ du modèle cible.

3. Approuver. Un plan seulement analysé ne s’exécute pas :

409 {"detail":"cannot move import plan from 'analyzed' to 'executed'"}

4. Essayer à blanc. C’est l’étape qui justifie tout le reste :

Fenêtre de terminal
curl -X POST "/api/v1/admin/imports/$PLAN/execute?dry_run=true" \
-H "Authorization: Bearer $TOKEN"
{
"dry_run": true,
"sources": [{
"target_model": "library_title",
"created": 1,
"skipped": 0,
"errors": [{
"row": 1,
"error": "duplicate key value violates unique constraint \"library_title_isbn_key\"\nDETAIL: Key (isbn)=(9780062316097) already exists."
}]
}]
}

Ce rapport vient d’un vrai essai : la deuxième ligne du fichier portait un ISBN déjà présent. La table n’a pas bougé d’une ligne. Corrigez le fichier, recommencez : l’erreur est par ligne, avec son numéro, donc on sait laquelle reprendre.

5. Exécuter, la même commande sans dry_run.

Rejouer ne duplique pas

Chaque ligne écrite est enregistrée sous une empreinte de son contenu, dans le registre d’identifiants externes. Relancer le même fichier :

0 created, 1 skipped

Ce qui vous concerne comme auteur de module

Presque rien, et c’est le but. Trois points seulement :

  • Un champ non nullable sans valeur par défaut rendra chaque ligne incomplète refusée. C’est correct, mais dites-le dans votre documentation d’accueil plutôt que de laisser un client le découvrir sur un fichier de 4 000 lignes.
  • Une relation se donne par son nom, pas par un identifiant : le noyau résout la valeur contre le modèle cible et crée l’enregistrement manquant si besoin. Un genre ou une catégorie s’importe donc en clair.
  • Une contrainte d’unicité est votre amie : c’est elle qui a arrêté l’essai ci-dessus. Un modèle sans unicité accepte les doublons en silence, et personne ne les verra avant le premier rapport faux.