Quand vous avez à faire un script pour un projet Django, il est pratique de l’avoir sous forme de sous-commande de manage.py
: c’est portable d’un projet à l’autre et ça permet d’utiliser l’ORM et les templates sans réglage puisqu’on a accès à tous les settings, automatiquement.
Malheureusement le wrapper de Django destiné à cela date un peu, il est plutôt lourd, pas très souple et utilise des pratiques qui ne sont plus au goût du jour depuis quelques années. Rien de rédhibitoire, mais tout de même.
D’abord, il faut placer sa commande dans un fichier portant le nom de la commande, et dans un package management.commands
d’une de vos apps.
Par exemple, si vous voulez faire une commande “nettoyer_godmichets” dans l’application “sex_shop”, il faudra la mettre bien profondément dans le tréfonds de votre projet :
racine │ ├── sex_shop │ │ ├── management │ │ │ ├── commands │ │ │ │ ├── nettoyer_godmichets.py │ │ │ │ ├── __init__.py │ │ │ │ └── __init__.pyc │ │ │ ├── __init__.py
Notez les fichiers __init__.py, indispendables sinon votre commande ne sera pas trouvée. Oh, et ‘sex_shop’ doit être dans INSTALLED_APPS
.
Ensuite, votre commande doit être une classe héritant de BaseCommand
. Sa méthode handle()
sera appelée automatiquement au lancement de la commande.
from django.core.management.base import BaseCommand from sex_shop.models import Godmichet class Command(BaseCommand): def handle(self, *args, **options): for god in Godmichet.objects.all(): god.clean() |
Et vous pouvez lancer la commande :
./manage.py nettoyer_godmichets |
Néanmoins généralement on voudra avoir un peu de décorum.
from optparse import make_option from django.core.management.base import BaseCommand from sex_shop.models import Godmichet, Marque class Command(BaseCommand): # ici on peut mettre un message d'aide help = 'Fait briller dard dard les dards' # optionellement une aide pour les arguments args = 'marque1, [marque2], marque3' # On peut ajouter des options passables à la commande option_list = BaseCommand.option_list + ( make_option('--dry-run', action='store_true', dest='dry_run', default=False, help='Affichage uniquement, aucune action réelle'), ) def handle(self, *args, **options): # on retrouve dans args les arguments positionnels de la commande if not args: for god in Godmichet.objects.all(): # Plutôt que print(), on peut utiliser ce wrapper # pour ecrire sur le terminal. Cela permet de bénéficier # automatiquement de l'option --verbosity self.stdout.write('Processing %s' % god.name) # La valeur des options est passée via kwargs. if not options['dry_run']: god.clean() else: # l'utilisateur a passé des marques ? On nettoie que les gods # de ces marques... for marque in args: try: gods = Marque.objects.get(name=marque).gods.all() for god in gods: self.stdout.write('Processing %s' % god.name) if not options['dry_run']: god.clean() except Marque.DoesNotExist: # si la marque n'existe pas, on fait une erreur # ceci arrête le script, retourne un code d'erreur 1 # et met le texte en rouge raise CommandError('La marque %s n'existe pas' %s marque) |
Et voilà, votre commande accepte maintenant optionnellement qu’on lui passe une liste de marques et/ou une option --dry-run
:
./manage.py nettoyer_godmichet devilmaycry choucroutebestfriend --dry-run |
En plus de ça, la commande accepte automatiquement :
--help
--verbosity
--settings
--pythonpath
Qui ont le même effet que sur toutes les commandes django officielles.
j’en cherchais une de commande pile poil hier pour flush la base de données (celle là meme qui est lancée quand on demarre les test unitaires). ya pas le les smartphones sur la meme longueur d’ondes ;)
Moi je me suis fais une commande fabric pour ça. Faudra que je poste mes recettes fabric un de ces 4.
Super, merci pour l’article !
(Petite erreur dans le code, handle prend **options et vous utilisez **kwargs dans la fonction)
Bonne continuation !
Bien vu man.
J’ajouterai ce petit snippet dans
/bin
pour ne plus avoir à aller cherchermanage.py
: