Ami·e qui écrit du code avec le framework Django, peut-être t'es-tu mis à utiliser le fantastique formateur de code black qui permet de se libérer les neurones d'un peu de bikesheding ?
Ton éditeur est bien configuré pour reformater ton code avec black automatiquement… Oui mais voilà… Quand tu génères tes migrations de base de donnée avec
./manage.py makemigrations
, tu dois repasser derrière pour formater le fichier .py de migration généré 😭.
Voici une solution pour que makemigrations
produise une migration correctement formatée au sens de black.
Alors déjà, si tu utilises Django en version 4.1 ou supérieure, il n'y a rien à faire, c'est inclus. Bonne journée 😘…
Dans le cas contraire, la bricole suivante permet de faire passer black automatiquement sur les migrations créées.
Surcharger la commande ./manage.py makemigrations
On va étendre le comportement de la commande makemigrations pour qu'elle inclue le reformatage.
Crée un fichier makemigrations.py à l'endroit suivant de ton projet Django (imaginons que le projet se nomme « monprojet ») :
monprojet/
├── management
│ ├── commands
│ │ ├── __init__.py
│ │ └── makemigrations.py
│ └── __init__.py
[…]
(NB: il faut créer les dossiers management, commands et les fichiers (vides) __init__.py si ils n'existent pas déjà).
Détail : on est en train de créer une commande django-admin, et comme elle possède le même nom que la commande makemigrations inclue avec Django, la notre va la « remplacer ».
… Avec le contenu suivant :
import glob
import subprocess
from django.core.management.commands.makemigrations import (
Command as CoreMakeMigrationsCommand,
)
def black(filepath: str):
"""Calls black for a given path/wildcard
assume black is installed and in PATH, will fail on exception otherwise…
:param filepath: can be an unix glob including wildcard in file paths.
"""
paths_list = glob.glob(filepath)
subprocess.check_call(["black", "-q", *paths_list])
class Command(CoreMakeMigrationsCommand):
def write_migration_files(self, changes):
ret = super(Command, self).write_migration_files(changes)
for app_name, migrations in changes.items():
for migration in migrations:
black(f"{app_name}/migrations/{migration.name}.py")
return ret
def handle_merge(self, *args, **kwargs):
super().handle_merge(*args, **kwargs)
# There is nowhere to get reliably the name of generated file
# So call on all "migrations" folders.
black(f"*/migrations")
Détail : on se contente d'hériter le fonctionnement de la commande originale en lui adjoignant une exécution de black par fichier de migration généré.
Il suffit ensuite d'utiliser ./manage.py makemigrations
comme d'habitude, joie 😎.
-
par exemple moi j'utilise uniquement les LTS, donc à la date de rédaction de cette article, la 3.2 ↩