PROJET AUTOBLOG


Sam et Max

source: Sam et Max

⇐ retour index

Lancer une fonction automatiquement à l’arrêt de Python

mardi 11 décembre 2012 à 03:38

Vous avez des envies de sortie grandiloquentes ? Ou juste besoin d’un peu de ménage à l’arrêt de la machine virtuelle de Python ?

Il y a un module pour ça™.

import atexit
 
# la version courte
@atexit.register
def strange_game():
    print "What about a nice game of chess ?"
 
 
def last_word(word="rosebud"):
    print word
 
# la version qui autorise des arguments
atexit.register(last_word, word="Monde de merde")

Les fonctions ne sont pas garanties d’être appelées dans un ordre précis.

(Très) Grand listing des libs standards les plus utiles en Python

lundi 10 décembre 2012 à 03:49

Dans la lignée du (Très) Grand listing des libs tierce partie les plus utiles en Python, voici la même chose mais pour les libs qui sont fournies en standard avec Python.

Bien entendu, il y a déjà une liste (bien plus complète), dans la documentation officielle. Mais la version ci-dessous vous propose uniquement les modules les plus utiles, en français, et surtout, avec une description de ce que vous pouvez faire avec (plutôt qu’une définition abstraite). Bonne découverte !

 

Sujet Nom de la bibliothèque Description
Texte
Extraction et formatage string Plein de choses dépréciées, mais une liste de constantes intéressantes comme par exemple tous les caractères ASCII en minuscule.
re Rechercher, extraire, splitter et remplacer avec des expressions rationnelles
difflib Analyser la différence entre deux textes
logging Puissant module pour créer des logs. Pratique pour stocker discrètement les erreurs, ou enregistrer l’activité de votre service dans un fichier.
fnmatch Vérifie si une chaîne correspond à une motif de recherche unix. Par exemple « *.py » va matcher script.py. Une alternative simple aux expressions rationnelles pour créer des filtres.
shlex Créer un petit parseur pour syntaxe régulière. On peut créer des mini-langages avec ou parser un fichier avec un format étrange.
Encodage codecs Opérations en vrac autour des encodages : lister les codecs disponibles, ouvrir un fichier dans un encodage précis, etc
unicodedata Principalement utile pour convertir les caractères spéciaux en leur équivalent ASCII. Ex : é => e
base64 Encode vers et décode base16, 32 et 64.
binascii Conversion du binaire vers l’ASCII et inversement.
Contrôle getpass Demander à l’utilisateur son mot de passe en ligne de commande, de manière sécurisée.
hashlib Sommes de contrôle en md5, sha1, sha224, sha256, sha384, sha512
hmac Vérifier authentification d’un message signé crytographiquement
Localisation gettext Boîte à outil pour fournir votre programme dans plusieurs langues. Ça ne vous dispense pas de traduire, mais ça remplace la traduction par la bonne version pour vous.
locale Quelques facilités pour adapter votre programme selon l’origine de votre utilisateur (détection de la région, formatage des chiffres, etc).
Types avancés
Manipulation du temps datetime Gestion des dates et des durées incluant la comparaison, l’addition, le formatage et le parsing.
time Informations sur l’horaire : fuseaux, heure d’hivers, etc.
calendar Permet de répondre à des question existentielles telle que : « 1996 était-elle une année bissextile ? » ou « combien y a-t-il de jour dans le mois suivant si j’avance de 67 jours ?»
Références weakref Créer une référence faible vers un objet de tel sorte que sa copie n’empêche pas le passage du garbage collector.
copy Mécanismes de copie superficielle et de copie complète des objets. L’assignation ne retournant qu’une référence, c’est utile si on veut une vrai copie.
Performance ctypes Appeler des fonctions C depuis Python. On peut même charger des .dll et des .so.
heapq Transforme une liste en un arbre binaire et permet de bénéficier de fort gain de performances sur certains algos de tris.
array Créer des tableaux typés (contrairement aux listes qui peuvent contenir des objets de plusieurs types). Beaucoup plus rapide à manipuler qu’une liste.
Collections collections Si les listes, tuples, dictionnaires et sets ne suffisent pas, ce module contient des tuples nommés, des dictionnaires ordonnés, des deques et des compteurs optimisés.
Queue Une file synchronisée utilisable pour partager des ressources qui vont être accédées de manière concurrente (ex : avec plusieurs threads).
pprint Affiche joliment les listes, les dictionnaires, etc. Très pratique pour débugger.
Maths
Calcul math Regroupe les fonctions mathématiques les plus courantes : puissance, exponentiel, log, etc.
cmath Comme le module math, mais pour les nombres complexes.
decimal Un type spécial qui permet de manipuler des chiffres à virgule sans le problème habituel de l’arrondi en informatique.
fractions Un type spécial qui permet de manipuler des fractions, et non leur résultat.
Génération pseudo aléatoire random Retourne des nombres « au hasard » selon des bornes ou des répartitions spécifiques.
uuid Génère un identifiant garanti d’être unique à travers le monde. Il y a 2 puissance 128 combinaison possibles.
Outils de développement
Concurrence threading Exécuter du code dans un thread séparé. Permet de ne pas bloquer si fait beaucoup d’I/O (téléchargement, lecture de fichiers, etc). Mais pas de gain sur la vitesse de calcul si on a un processeur à plusieurs cœurs.
multiprocessing Exécuter du code dans un processus séparé. Mêmes bénéfices que les threads, en moins léger, mais avec un gain de vitesse sur les calculs si on a un processeur à plusieurs cœur.
signal Envoyer des signaux à son propre programme. Par exemple, interrompre une opération si elle prend trop de temps.
Debugging pdb Debuggueur intégré à Python
timeit Calculer le temps que met un code à s’exécuter
trace Suivre ce que fait un programme, pas à pas
Boîte à outils itertools Toute sorte de manipulation sur les itérables : chaîner, lire en boucle, dupliquer, lire un morceau sous condition, etc
functools Outils pour faciliter la programmation fonctionelles : fonctions partielles, copie de metadata d’une fonction vers une autre, etc
operator Tous les opérateurs (+, %, &, [], >>, …) sous forme de fonction.
argparse Automatise le parsing des arguments passés à votre script. Génère « –help » automatiquement. Gère les sous-commandes et les vérifications essentielles.
atexit Demander à Python d’exécuter une fonction quand il s’arrête.
contextlib Outils pour créer des context managers (ce qui s’utilise avec le mot clé « with »). Permet notamment de créer facilement un context manager à partir d’un simple générateur.
sched Files d’attentes dans laquelle on met à la queue des fonctions à exécuter plus tard.
Code pydoc Générer automatiquement la documentation depuis votre code
doctest Lancer des tests unitaires à partir des exemples écrits dans les docstrings de votre programme.
unittest Le standard pour les tests unitaires en Python
2to3 Automatise la conversion d’un code Python 2 en un code Python 3. Ne fait pas tout le travail, mais fait gagner beaucoup de temps. Un outil inverse existe : 3to2.
Introspection inspect Permet d’obtenir toutes les meta informations possibles à propos de toutes les objets du programme. Contient notamment des fonctions pour savoir si un objet est un générateur, un module, ou une classe.
abc Une implémentation des classes abstraites en Python. Pas vraiment indispensable, mais si vous rencontrez du code qui l’utilise, vous saurez à quoi ça sert.
gc Manipuler le garbage collector. Par exemple vous pouvez le désactiver.
warnings Module principalement importé par les gens qui en ont marre de voir leur console pleine de DeprecationWarning.
traceback A garder sous le coude pour le jour où vous aurez à attraper une erreur, et afficher d’où elle viens dans le code dans votre propre format.
__future__ Facilite la transition vers les version suivantes de Python en autorisant l’import de fonctionnalités optionnelles qui deviendront obligatoires plus tard. Ainsi, from __future__ import print_function permet d’utiliser la fonction print() de Python 3 en Python 2.7.
__builtin__ Contient toutes les fonctions built-in de Python : open, str, list, all, etc. Peut utile, sauf pour se rafraîchir la mémoire ou créer un système d’autocomplétion.
Fichiers et dossiers
Système de fichiers os Très gros module qui va de la manipulation de chemin et nom de fichiers à la vérification de permissions.
stat Contient des fonctions pour analyser les appels de os.stats. Surtout utile pour gagner en performance et éviter de multiples appels si on est intéressé par plusieurs types d’informations sur un fichier. Sinon, utiliser plutôt les fonctions de os autre que os.stat.
mimetypes Deviner le type d’un fichier selon son mimetype déclaré.
shutil Opération en masse sur les fichiers comme supprimer récursivement une arborescence
mailcap Définir qu’il faut ouvrir un programme pour gérer une fichier de tel mimetype est fait à travers à mailcap, que ce module permet de manipuler.
glob Lister les fichiers d’un répertoire en utilisant la syntaxe de filtre d’Unix. Ex : « *.py » va lister tous les fichiers Python.
filecmp Compare des arborescences et permet de traiter leurs différences.
tempfile Créer des dossier et des fichiers temporaires.
Contenu de fichiers linecache Récupérer un ligne n’importe où dans un fichier, efficacement.
io Fournit des objets buffers. Entre autre utile pour cumuler des chaînes de caractères ou manipuler un objet avec l’interface d’un fichier en mémoire vive.
mmap Manipuler un fichier réel comme si il était en mémoire vive, à travers l’interface habituelle des fichiers, ou comme une chaîne de caractères mutable.
Persistance pickle Sérialiser n’importe quelle structure de données Python. Permet de transformer en texte des objets de toute sorte, et inversement. La version en C du module(cPickle), parfois disponible, est plus rapide.
shelve Un dictionnaire, mais qu’on peut sauvegarder sur le disque, et recharger plus tard.
sqlite3 Créer, lire et écrire dans une base de données SQLite, qui est une base de données complète, ACID, supportant presque tout SQL92.
Système
Interfaces utilisateur curses Créer des interfaces en ligne de commande
readline Implémenter un historique ou de l’autocompletion pour une UI en ligne de commande.
cmd Facilite la création d’un shell avec des mots clés en guise de commande
webbrowser Ouvre le navigateur Web par défaut à l’adresse donnée.
Tkinter Interface graphique par défaut fournie avec Python. Assez pauvre et mal intégrée à l’OS.
Installation sys Informations sur le votre environnement : variable d’environnement, paramètres du script, limite de récursion, chemin d’import, etc
platform Tout ce que Python sait sur la machine qu’il l’héberge : OS, version, processeur, etc.
sysconfig Nombreuses informations sur l’installation de Python : flag de compilation, version, dossiers d’installation, etc.
Processus errno Code d’erreurs utilisés par Python lorsqu’un processus échoue. Utile pour dissocier plusieurs types d’IOError.
subprocess Exécuter une programme externe.
Formats
Compression zlib Lire et écrire le format bzip
gzip Lire et écrire le format gzip
bz2 Lire et écrire le format bzip2
zipfile Lire et écrire des fichiers zip
tarfile Lire et écrire des fichiers tar
Langages de balise sgmllib Parser du SGML. Le format est surtout utilisé par l’industrie du livre, donc vous le rencontrerez peu.
htmllib Parser du HTML. Il est presque toujours mieux d’utiliser une bibliothèque tierce partie pour cela.
htmlentitydefs Une liste de entités HTML pour ISO Latin-1 et des fonctions pour convertir dans un sens et dans l’autre. Résout parfois des problèmes d’encodage épineux.
xml.etree.ElementTree Manipulation d’un fichier XML : analyse, création, export, etc. Sax (lecture rapide) et Dom (création via l’API standard) sont disponibles mais je recommande plutôt ElementTree pour la lecture (ou lxml si vous pouvez l’installer) et un langage de template pour la production.
Autres formats csv Lire et produire des fichiers CSV. DictReader est particulièrement utile, car il prend en compte les en-tête. Suffisant pour 80 % des cas où l’utilisateur veut un fichier compatible Excel.
ConfigParser Lire et écrire les fichiers INI. Principalement utilisé pour la configuration.
robotparser Lire les fichiers robot.txt.
json Transforme du JSON en type natif Python, et inversement.
plistlib Lire les fichiers de configurations binaires de Mac OS.
Réseau
J’évite ici volontairement les très nombreux module qui ont pour but de vous permettre d’implémenter votre propre serveur pour le protocole X ou Z. Si vous lisez cet article, vous n’en avez probablement pas besoin.
Communication entre machines socket Module de manipulation bas niveau des sockets en Python. À moins de savoir exactement ce que vous faite, je vous conseille plutôt d’utiliser PyZmq ou Twisted.
asyncore Boîte à outil fournissant des outils basiques pour la programmation asynchrone en Python. Même remarque que précédement.
asynchat Exemple d’implémentation d’une communication client / serveur avec asyncore. Même remarque que précédemment
ssl Permet de se connecter à un serveur de manière sécurisée en utilisant le chiffrement SSL.
Email email Parser et créer des emails. L’email est un format complexe. Ne faites pas ça à la main. Utiliser ce module.
mailbox Lire et écrire dans différent format de mail box, c’est à dire les formats de stockage des emails par les clients mails tels que mbox.
poplib Se connecter et interagir avec un serveur POP pour lire les mails
imaplib Se connecter et interagir avec un serveur IMAP pour lire les mails
smtplib Se connecter et interagir avec un serveur SMTP pour envoyer des mails. Notez qu’il faut plus qu’un code qui marche pour envoyer des mails du fait des nombreux garde-fous pour limiter le spam.
Web urlparse Extraire les différentes parties d’une URL
SimpleHTTPServer Lancer un petit serveur HTTP. Très pratique pour les tests ou pour partager un fichier sur son réseau local. Ne pas utiliser ça en production.
Cookie Lire et écrire dans le format propre aux cookies.
cookielib Gestion automatique des cookies. A utiliser en conjonction avec urllib et urllib2 pour simuler un utilisateur réel. Vu la complexité de l’interface, installez plutôt mechanize et requests si vous le pouvez.
urllib Et urllib2 Permet de faire des requêtes sur une URL donnée et lire la réponse. Les deux modules se recoupent largement, mais certaines fonctionalités sont disponibles dans l’un ou l’autre. Même remarque concernant la complexité que pour le module précédent.
httplib Bibliothèque de bas niveau pour travailler avec HTTP, et utilisée par urllib. Rarement utile, sauf parfois pour importer une exception.
Autres protocoles ftplib Créer un client FTP pour envoyer et recevoir des fichiers
nntplib Créer un client NNTP pour lire des news sur Usenet
telnetlib Créer un client telnet. Utile pour controler du vieux matiriel à distance, comme certains routeurs.
xmlrpclib Créer un client XMLRCP pour appeler des méthodes d’un objet dans un autre programme. Surtout utilisé dans le monde Java.

Il n’y a aucune loi qui interdise de nettoyer un tunnel

dimanche 9 décembre 2012 à 01:24

Parfois votre client va vous demander un truc tordu, ou vous poser des contraintes à la con. Parfois votre cousin relou vient passer le WE la même date que votre plan cul. Parfois on donne une formation Python sous Windows…

Quand ça m’arrive, j’essaye de me souvenir de l’exemple d’Alexandre Orion, un artiste Brésilien qui a été plusieurs fois interpellé brutalement par la police pour avoir décoré, petit à petit, les murs d’un tunnel à fort passage avec un énorme ossuaire.

Sa protestation contre la pollution ne l’a néanmoins jamais mené en prison, car il connaissait les règles. Il a juste un peu triché.

Au lieu de tagger des os sur les murs, il a choisi les endroits les plus sales, et a nettoyé la surface jusqu’à laisser apparaître la couleur blanche sous-jacente, dessinant par extrusion des cranes bien visibles.

La police l’a chopé plusieurs fois, mais il n’a jamais eu de problème avec la justice, car il n’y a pas de loi qui interdise de nettoyer un tunnel.

La prochaine fois que votre client vous demande un thème rose fluo, que votre meuf vous invite au dîner dominicale avec belle-maman, que votre pote super adroit vient vous emprunter votre bagnole, rappelez-vous qu’il y peut-être quelque chose à jouer avec la contrainte.

Tout comme il n’y a aucun courage à ne pas avoir peur, car le courage c’est de se bouger le cul malgré la trouille, on est pas créatif si on à les mains complètement libres. Ne vous plaignez pas que la situation n’es pas idéale, changez juste légèrement plan.

Beau et facile à utiliser: deux choses bien différentes

vendredi 7 décembre 2012 à 12:24

Je suis ergonome de formation. La première chose que j’ai apprise, c’est que mon intuition est à chier. Dit très clairement: si je design un site Web basé sur ce que je pense être utilisable, je vais me planter.

Même après des années d’expérience, mes premières idées sont très classiques, formatées, et Max me met des taquets derrière la tête régulièrement en guise de rappel.

Pour rendre une interface utilisable, il n’y a qu’un moyen fiable: le test. Pour apprendre en une journée tout ce que sais de l’ergonomie, vous pouvez en fait plus ou moins vous limiter à lire et appliquer le bouquin culte de Steve Krug.

Et c’est quelque chose que les designers ont du mal à comprendre. Ils font souvent des choses très belles, mais n’est pas Apple qui veut. Faire quelque chose de beau ET d’utilisable est très difficile.

Et si on doit choisir, il vaut mieux faire utilisable que beau.

Les états

Les états sont par exemple un vrai problème. En informatique, il n’y a pas vraiment de moyen de connaître l’état de quelque chose, et donc on crée artificiellement des repères visuels en espérant que l’utilisateur comprenne ce que l’on veut dire.

Seulement beau n’est pas forcément explicite. Est-ce que quelqu’un peut me dire ici quel bouton correspond à l’état “on” ?

Bouton on / off

On / Off, l'état le plus simple du monde

Le premier bouton, il est sur “off” parce qu’il affiche “off” ou il est sur “on”, offrant la possibilité de slider vers l’état “off” ? Les deux comportements existent dans le monde matériel.

Nous avons déjà des boutons qui reflètent très bien on / off: les checkboxes. Tout le monde sait comment ça marche, mais c’est moche, alors la plupart des designers essayent de les remplacer.

Si la designite vous démange, il faut alors sérieusement réfléchir, et ne pas proposer une solution moins efficace que ce qui existe déjà. Par exemple, une bonne implémentation du concept ci-dessus est:

Slide on / off avec effet lumineux

Un tout petit détail...

L’auteur a ici ajouté un simple effet grisé sur le “off” (qui a la connotation de “désactivé”) et un effet de lumière sur le “on” (qui à la connotation de “en fonctionnement”). On sait clairement que le premier est l’état “off”, et que si on clique dessus, on va obtenir le second état: “on”.

Mais les états ne sont pas toujours aussi simples que on / off, et surtout, ils se cachent parfois dans des situations naturelles. 37signals ont largement défendu l’idée de parer à ce que l’utilisateur va vouloir faire, plutôt que d’attendre qu’il le fasse. Et cela passe par la prédictibilité, et la compréhension du contexte, donc par l’affichage d’états et d’intentions.

Un exemple simple: l’état vide. Vous n’imaginez pas combien il est courant de ne pas travailler l’état de départ, ou vide, d’une UI. Pourtant, c’est primordial car c’est le premier contact de l’utilisateur avec votre logiciel, et surtout, par principe, vide signifie qu’il n’y a aucun indice sur ce qu’il faut faire par défaut.

Prenez l’explorateur de fichiers par défaut d’Ubuntu, Nautilus. Il est globalement très bon. En revanche, sur les gros dossiers, ou sur les dossiers contenant beaucoup de thumbnails, il y a un instant plus ou moins long durant lequel on ne sait pas si il charge, ou si le dossier est vide.

Capture d'écran de Nautilus dans un dossier vide

Il n'y a rien dans ce dossier

Une fois qu’on a décidé que l’état “n’affiche rien” était bien non un temps de chargement, mais le fait qu’il n’y a rien à afficher, le cerveau fait la déduction, “le dossier est vide”.

Prenez par contre Marlin, l’explorateur d’Elementary OS, et donc bien moins intégré à Unity, dans lequel il ressort plus laid que Nautilus. Mais il vous montre instantanément de quoi il est question.

Capture d'écran de Marlin dans un dossier vide

Marlin est un projet très prometteur

Ceci vous parait un détail. En fait, certains geeks trouvent même énervant tous ces trucs qui “ne servent à rien” pour “les gens qui ne veulent pas réfléchir” car “c’est quand même pas compliqué de voir que le dossier est vide”.

Oui, sauf que comme je vous en parlais précédemment, il existe une telle chose que la charge cérébrale, et que tout ce qui y participe se cumule.

Faire un beau design allège la charge cérébrale, mais faire un design utilisable la soulage sans commune mesure. Qui dit moins de charge, dit une utilisation fluide, sans peur (car oui, la peur de cliquer est un facteur étonnamment important dans le schéma de décision des utilisateurs lambdas), et donc des users qui aimeront votre app.

Le but est d’obtenir le même résultat que le crayon: quand vous écrivez, vous pensez à ce que vous écrivez, pas au crayon. Bien sûr que réfléchir un tout petit peu n’est pas la mer à boire, mais c’est autant de neurones que vous consacrez à votre outil plutôt qu’à votre travail.

Mais faire un design, plus que beau, utilisable, c’est aussi proposer à l’utilisateur ce qu’il peut faire. Mettre en avant les solutions les plus importantes. Les menus ajoutent à la charge cérébrale, ce sont des accès secondaires.

UsabilityFriction a posté une bonne illustration de la démarche. Ils sont partis d’un truc vide et pas top:

Mockup d'un porte folio

Vous n'avez pas de photo. Merci. Au revoir.

Et ils ont ajouté:

Mockup d'un porte folio amélioré

Vous n'avez pas de photos. Mais vous pouvez faire tout ça !

Ici il n’y a pas d’enjeu esthétique car c’est un mockup. Et c’est tout l’interêt du mockup pourri, fait avec balsamiq ou même au feutre Veleda sur brouillon: on se soucie de l’utilisabilité, pas du look. Le premier est la priorité car un bon designer rendra votre site qui est déjà facile à utiliser beau, alors que rendre un site déjà designé facile à utiliser demande beaucoup plus de travail additionnel.

Un bon dessin vaut mieux qu’un long discours, sauf si on dessine mal

Utiliser des icônes, c’est bien, non ?

En théorie, les formes et couleurs sont plus faciles et rapides à identifier que des mots. Et on évite les problèmes de traduction.

Mais, le hic, c’est que la signification d’une icône, contrairement à un mot, n’est pas garantie:

Et si on choisit les icônes, on est obligé d’en mettre de manière cohérente, donc quand on rajoute un nouveau bouton, c’est une nouvelle icône à rajouter: il faut en trouver une correspondante, et au sens, et au design.

Un exemple: j’aime le nouveau design de Gmail. Mais le tout icône n’est pas forcément une réussite. Dans l’ancienne version, trouver comment marquer un mail en tant que spam était évident:

Capture d'écran de l'ancien menu mail de Gmail

Le mot spam est très facile à reconnaître: court et à faible ambiguïté

Le nouveau design est plus beau, mais par contre, pour mettre son mail en spam…

Capture d'écran du nouveau menu mail de Gmail

Difficile de faire le lien entre un hexagone avec un point d'exclamation et "spam"

L’objectif n’est pas de pinailler sur les qualités de x ou z, mais d’illustrer. Stackoverflow est un modèle d’utilisabilité, mais le site comporte pourtant très peu d’icônes. Mnmlist prouve qu’on peut être jusqu’au boutiste pour aller à l’essentiel. Drudge report prouve que… heu… Je vous laisse lire.

Encore une fois, ne laissez pas la fièvre graphique prendre la priorité sur l’utilisabilité. Un beau design est important. Un design utilisable est primordial. Il n’existe pas de solution clé en main telle que “utilisons des icônes” qui pallie le travail de réflexion ergonomique dont votre projet à besoin.

Tous les types de designs ont leur place: avec et sans icônes, avec des images, sans, avec du texte, sans, avec des jeux de couleur, sans. La recette à elle seule ne peut pas rendre un plat délicieux, il faut de bons cuisiniers, de bons ingrédients et du travail.

(et la métaphore de la bouffe est particulièrement adaptée, car quel que soit l’acharnement que vous mettez à votre popotte, y aura toujours un connard pour trouver ça dégueulasse)

Un dernier exemple de “ça dépend”. Gedit (Gnome) VS Scratch (Elementary OS), deux éditeurs de texte, deux approches pour “l’état de départ”, aucune n’est meilleure que l’autre, et elles ne satisferont pas le même public.

Capture d'écran de Gedit sur un fichier vide

Je préfère l'approche de Gedit: on commence cash à éditer, c'est plus efficace

 

Capture d'écran de Scratch sur un fichier vide

Un non dev sera parfois plus à l'aise avec une indication claire de ce qui va se passer

Tout comme on oriente son design en fonction de son public (hacker news peut se permettre l’aspect WEB 1.0, pas Facebook), l’ergonomie doit viser aussi le type d’utilisateur auquel vous souhaitez plaire. Car il n’y a aucune chance que vous plaisiez à tout le monde.

Utiliser des UUID comme primary key avec l’ORM de Django

jeudi 6 décembre 2012 à 15:59

Par défaut Django ajoute automatiquement un champ id à tous les modèles, et le configure pour être un entier qui s’auto incrémente puis le désigne comme la clé primaire. Il est néanmoins possible d’utiliser un autre champ comme clé primaire pour sa table: un slug ou un identifiant métier. Dans notre cas, on va voir comment utiliser un UUID.

Un UUID est un identifiant généré de manière pseudo aléatoire qui a une forte probabilité d’être unique dans le monde entier, il y a 4×10³⁷ combinaisons possibles pour les versions des algos récents. Selon le paradoxe des anniversaires, il faudrait créer un milliard de UUID par seconde pendant 100 ans pour que le prochain ait 50% de chance d’être un doublon. Normalement pour votre site de fans club des limules hermaphrodites, ça devrait être suffisant.

Utiliser des UUID possède de nombreux avantages car ils sont plus ou moins garantis d’être uniques, et ainsi:

Pour toutes ces raisons, les bases de données NoSQL (CouchDB, MongoDB, etc) utilisent depuis longtemps les UUID comme clés primaires par défaut. Le moteur de base de données de Google, Big Table, utilise des UUID.

Pourtant les UUID ne sont pas exempts de défauts:

Les UUID ne sont donc pas la solution miracle à tous les soucis, mais ils sont tout de même mon choix par défaut en ce moment ne serait-ce que pour les fixtures. Je gagne les performances sur le caching et le parallélisme, et un peu de lenteur sur les JOINS est quelque chose que je peux supporter. Pour les gros sites, les JOINS sont de tout façon votre ennemi juré et on finit toujours par tweaker à grand coup de dé-normalisation.

On peut faire passer ses modèles Django aux UUID en faisant:

from uuid import uuid4
 
class MotDElle(object):
    ...
    id = models.CharField(max_length=36, primary_key=True,
                          default=lambda: str(uuid.uuid4()), editable=False)

Ou, si vous utilisez django_extensions (et vous devriez, ne serait-ce que pour shell_plus et runserver_plus):

from django_extensions.db.fields import UUIDField
 
class MotDElle(object):
    ...
    id = UUIDField(primary_key=True)

Ce qui a l’avantage de caster la valeur en un objet UUID, et non une bête string, à la lecture.

Malheureusement, pour le moment il n’y a aucune implémentation officielle de Django pour utiliser les UUID. Cela a des conséquences fort ennuyeuses:

Un ticket pendouille depuis 5 ans sur la question. Je vous invite d’ailleurs à le spammer jusqu’à ce que réouverture s’en suive. Et le premier qui me dit “t’as qu’à l’implémenter, toi”, je lui retourne un tampon dans la poire.