• en
  • Langue : fr

Écriture de commandes django-admin personnalisées

Les applications peuvent inscrire leurs propres actions auprès de manage.py. Par exemple, il pourrait être utile d’ajouter une action manage.py pour une application Django que vous distribuez. Dans ce document, nous allons créer un commande personnalisée closepoll pour l’application polls écrite dans le tutoriel.

Pour cela, ajoutons simplement un répertoire management/commands dans l’application. Django inscrit une commande manage.py pour chaque module Python dans ce répertoire dont le nom ne commence pas par un soulignement. Par exemple :

polls/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            _private.py
            closepoll.py
    tests.py
    views.py

Avec Python 2, assurez-vous d’inclure des fichiers __init__.py dans les répertoires management et management/commands comme cela est fait ci-dessus, sinon votre commande ne sera pas détectée.

Dans cet exemple, la commande closepoll sera disponible dans tout projet qui inclut l’application polls dans son réglage INSTALLED_APPS.

Le module _private.py ne sera pas disponible en tant que commande de gestion.

Le module closepoll.py n’a qu’une seule exigence : il doit définir une classe Command comme sous-classe de BaseCommand ou l’une de ses sous-classes.

Scripts autonomes

Les commandes de gestion personnalisées sont particulièrement utiles pour exécuter des scripts autonomes ou pour des scripts qui sont programmés pour s’exécuter périodiquement par l’intermédiaire du crontab Unix ou du panneau de configuration des tâches planifiées de Windows.

Pour implémenter la commande, modifiez polls/management/commands/closepoll.py comme ceci :

from django.core.management.base import BaseCommand, CommandError
from polls.models import Poll

class Command(BaseCommand):
    help = 'Closes the specified poll for voting'

    def add_arguments(self, parser):
        parser.add_argument('poll_id', nargs='+', type=int)

    def handle(self, *args, **options):
        for poll_id in options['poll_id']:
            try:
                poll = Poll.objects.get(pk=poll_id)
            except Poll.DoesNotExist:
                raise CommandError('Poll "%s" does not exist' % poll_id)

            poll.opened = False
            poll.save()

            self.stdout.write('Successfully closed poll "%s"' % poll_id)
Changed in Django 1.8:

Avant Django 1.8, les commandes d’administration étaient basées sur le module optparse, et les paramètres positionnels étaient transmis dans *args alors que les paramètres nommés étaient passés dans **options. Maintenant que les commandes d’administration utilisent argparse pour le traitement des paramètres, tous les paramètres sont transmis par défaut dans **options, sauf dans le cas où vous nommez vos paramètres positionnels args (mode de compatibilité). Nous vous encourageons à n’utilisez que **options pour toute nouvelle commande.

Note

Quand vous utilisez des commandes de gestion et que vous souhaitez afficher des informations en console, vous devriez utiliser les méthodes self.stdout et self.stderr plutôt que d’utiliser directement stdout et stderr. En utilisant ces méthodes intermédiaires, il devient beaucoup plus simple de tester vos commandes personnalisées. Notez aussi que vous n’avez pas besoin de terminer vos messages avec un caractère de saut de ligne car il sera ajouté automatiquement, sauf si vous indiquez le paramètre ending:

self.stdout.write("Unterminated line", ending='')

La nouvelle commande personnalisée peut être appelée en utilisant python manage.py closepoll <poll_id>.

La méthode handle() reçoit une ou plusieurs poll_ids et définit poll.opened à False pour chacun d’eux. Si l’utilisateur indique des identifiants qui n’existent pas, une erreur CommandError est levée. L’attribut poll.opened n’existe pas dans le tutoriel, elle a été ajoutée à polls.models.Poll pour cet exemple.

Définition de paramètres facultatifs

La même commande closepoll pourrait aisément être modifiée afin de supprimer un sondage donné au lieu de le fermer, en acceptant des options supplémentaires en ligne de commande. Ces options personnalisées peuvent être ajoutées dans la méthode add_arguments de cette façon :

class Command(BaseCommand):
    def add_arguments(self, parser):
        # Positional arguments
        parser.add_argument('poll_id', nargs='+', type=int)

        # Named (optional) arguments
        parser.add_argument('--delete',
            action='store_true',
            dest='delete',
            default=False,
            help='Delete poll instead of closing it')

    def handle(self, *args, **options):
        # ...
        if options['delete']:
            poll.delete()
        # ...
Changed in Django 1.8:

Précédemment, seule la bibliothèque standard optparse était prise en charge et il fallait alors étendre la variable de commande option_list avec optparse.make_option().

L’option (delete dans notre exemple) est disponible dans le paramètre dictionnaire options de la méthode handle(). Consultez la documentation Python de argparse pour en savoir plus sur l’utilisation de add_argument.

En plus de pouvoir ajouter des options de ligne de commande personnalisées, toutes les commandes de gestion acceptent certaines options par défaut, telles que --verbosity et --traceback.

Commandes de gestion et langues

Par défaut, la méthode BaseCommand.execute() désactive les traductions car certaines commandes fournies par Django effectuent plusieurs tâches qui exigent une langue de projet neutre, par exemple le rendu de contenu utilisateur ou le remplissage de la base de données.

Changed in Django 1.8:

Dans les versions précédentes, Django forçait la locale « en-us » au lieu de désactiver les traductions.

Si pour une raison ou une autre votre commande de gestion a besoin d’utiliser une locale bien précise, c’est à vous de l’activer et de la désactiver manuellement dans votre méthode handle() en utilisant les fonctions offertes par le code de prise en charge de l’internationalisation :

from django.core.management.base import BaseCommand, CommandError
from django.utils import translation

class Command(BaseCommand):
    ...
    can_import_settings = True

    def handle(self, *args, **options):

        # Activate a fixed locale, e.g. Russian
        translation.activate('ru')

        # Or you can activate the LANGUAGE_CODE # chosen in the settings:
        from django.conf import settings
        translation.activate(settings.LANGUAGE_CODE)

        # Your command logic here
        ...

        translation.deactivate()

Un autre besoin pourrait être que votre commande doive simplement utiliser la langue définie dans les réglages et que Django soit empêché de la désactiver. Vous pouvez faire cela en indiquant l’option BaseCommand.leave_locale_alone.

En travaillant sur la base des scénarios décrits ci-dessus, soyez toutefois conscient que les commandes de gestion système doivent être très prudentes quand elles sont lancées avec des locales changeantes, soyez donc attentif aux points suivants :

  • Assurez-vous que le réglage USE_I18N est toujours True lorsque la commande fonctionne (c’est un bon exemple des problèmes possibles dus à un environnement d’exécution dynamique, ce que les commandes Django évitent en désactivant les traductions).

  • Passez en revue le code de votre commande et le code qu’elle appelle quant à des différences de comportement lorsque les locales sont modifiées et évaluez l’impact potentiel sur le comportement prévisible de votre commande.

Tests

Des informations sur la manière de tester des commandes d’administration personnalisées peuvent être consultées dans la documentation sur les tests.

Objets de commandes

class BaseCommand

La classe de base dont toutes les autres commandes de gestion héritent.

Utilisez cette classe si vous souhaitez accéder à tous les mécanismes qui analysent les paramètres de ligne de commande et qui aiguillent sur le code à appeler en conséquence. Si vous n’avez pas besoin de modifier ce comportement, considérez l’utilisation de l’une de ses sous-classes.

L’héritage direct de la classe BaseCommand exige que vous implémentiez la méthode handle().

Attributs

Tous les attributs peuvent être définis dans votre sous-classe et peuvent être utilisés dans les sous-classes de BaseCommand.

BaseCommand.args

Une chaîne énumérant les paramètres acceptés par la commande, adaptée à l’utilisation des message d’aide ; par exemple, une commande qui accepte une liste de noms d’applications pourrait définir cette chaîne à « <app_label app_label ...> ».

Obsolète depuis la version 1.8: Cela doit dorénavant être fait par la méthode add_arguments(), en appelant la méthode parser.add_argument(). Voir l’exemple closepoll ci-dessus.

BaseCommand.can_import_settings

Une valeur booléenne indiquant si la commande doit pouvoir importer les réglages Django. Si True, execute() vérifiera que c’est bel et bien possible avant de continuer. La valeur par défaut est True.

BaseCommand.help

Une brève description de la commande, qui sera affichée dans le message d’aide lorsque l’utilisateur lance la commande python manage.py help <commande>.

BaseCommand.missing_args_message
New in Django 1.8.

Si votre commande définit des paramètres positionnels obligatoires, vous pouvez personnaliser le message d’erreur renvoyé au cas où le paramètre serait absent. Le message par défaut est produit par argparse (« too few arguments » (trop peu de paramètres)).

BaseCommand.option_list

Une liste des options optparse qui seront transmises dans l’OptionParser de la commande pour l’analyse des paramètres.

Obsolète depuis la version 1.8: Vous devez maintenant surcharger la méthode add_arguments() pour ajouter des paramètres personnalisés acceptés par votre commande. Voir l’exemple ci-dessus.

BaseCommand.output_transaction

Une valeur booléenne indiquant si la commande affiche des commandes SQL ; si True, le contenu en sortie est automatiquement entouré des instructions BEGIN; et COMMIT;. La valeur par défaut est False.

BaseCommand.requires_system_checks
New in Django 1.7.

Un booléen ; si True, l’ensemble du projet Django sera vérifié, à la recherche de problèmes potentiels, avant d’exécuter la commande. Si requires_system_checks est manquant, la valeur de requires_model_validation est utilisée. Si cette dernière option est aussi manquante, la valeur par défaut (True) est utilisée. Définir à la fois requires_system_checks et requires_model_validation se traduit par une erreur.

BaseCommand.requires_model_validation

Obsolète depuis la version 1.7: Remplacé par requires_system_checks

Une valeur booléenne ; si True, les modèles installés seront validés avant l’exécution de la commande elle-même. La valeur par défaut est True. Pour valider les modèles d’une seule application plutôt que les modèles de toutes les applications, appelez vous-même validate() à partir de handle().

BaseCommand.leave_locale_alone

Une valeur booléenne indiquant si la langue définie dans les réglages doit être préservée durant l’exécution de la commande pour empêcher d’être forcée à « en-us ».

La valeur par défaut est False.

Soyez sûr de savoir ce que vous faites si vous décidez de changer la valeur de cette option pour votre propre commande et que celle-ci crée du contenu de base de données variant en fonction de la langue et que ce contenu ne doit pas être traduit (comme cela se passe avec les permissions de django.contrib.auth), car si la langue active est différente de la valeur « en-us » par défaut, vous pourriez rencontrer des problèmes inattendus. Consultez la section Commandes de gestion et langues ci-dessus pour des détails plus complets.

Cette option en peut pas être False lorsque l’option can_import_settings est aussi définie à False parce que le processus de changement de langue a besoin d’accéder aux réglages. Cette condition provoque une erreur CommandError.

BaseCommand.style

Un attribut d’instance aidant à colorer le contenu envoyé aux sorties stdout ou stderr. Par exemple :

self.stdout.write(self.style.NOTICE('...'))

Voir Syntaxe colorée pour savoir comment modifier la palette de couleurs et pour connaître les styles disponibles (utilisez les versions en majuscules des « rôles » décrits dans cette section).

Si vous passez l’option --no-color lors du lancement de la commande, tous les appels à self.style() renverront la chaîne originale sans couleur.

Méthodes

BaseCommand contient quelques méthodes qui peuvent être surchargées, mais seule la méthode handle() doit absolument être implémentée.

Implémentation d’un constructeur dans une sous-classe

Si vous implémentez __init__ dans votre sous-classe de BaseCommand, vous devez appeler la méthode __init__ de BaseCommand:

class Command(BaseCommand):
    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)
        # ...
BaseCommand.add_arguments(parser)
New in Django 1.8.

Point d’entrée pour ajouter des paramètres acceptés lors du traitement des paramètres passés en ligne de commande. Les commandes personnalisées doivent surcharger cette méthode pour ajouter les paramètres, qu’ils soient positionnels ou nommés, que cette commande va accepter. L’appel de super() n’est pas nécessaire lorsqu’on hérite directement de BaseCommand.

BaseCommand.get_version()

Renvoie la version de Django, qui devrait être correcte pour toutes les commandes intégrées de Django. Les commandes fournies par les applications non Django peuvent surcharger cette méthode pour retourner leur propre version.

BaseCommand.execute(*args, **options)

Essaie d’exécuter cette commande en procédant aux contrôles systèmes au besoin (selon la valeur de l’attribut requires_system_checks). Si la commande génère une erreur CommandError, celle-ci est interceptée et affichée sur stderr.

Appel d’une commande gestion depuis son propre code

execute() ne devrait pas être appelée directement depuis votre code pour lancer une commande. Utilisez plutôt call_command.

BaseCommand.handle(*args, **options)

Le corps de la logique de votre commande. Les sous-classes doivent implémenter cette méthode.

Elle peut renvoyer une chaîne Unicode qui sera envoyée vers stdout (encadrée par BEGIN; et COMMIT; si output_transaction vaut True).

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)
New in Django 1.7.

Utilise l’infrastructure de vérification système pour inspecter l’ensemble du projet Django à la recherche de problèmes potentiels. Les problèmes graves génèrent des exceptions de type CommandError; les avertissements sont émis sur la sortie d’erreur (stderr) ; les notifications mineures sont émises sur la sortie standard (stdout).

Si app_configs et tags valent tous deux None, tous les contrôles système sont effectués. tags peut être une liste d’étiquettes de contrôle, comme compatibility ou models.

BaseCommand.validate(app=None, display_num_errors=False)

Obsolète depuis la version 1.7: Remplacée par la commande check

Si app vaut None, un contrôle d’erreurs est effectué pour toutes les applications installées.

Sous-classes de BaseCommand

class AppCommand

Une commande de gestion qui accepte en paramètre une ou plusieurs étiquettes d’applications installées, et applique un traitement à chacune d’elles.

Plutôt que d’implémenter handle(), les sous-classes doivent implémenter handle_app_config(), qui sera appelée une seule fois pour chaque application.

AppCommand.handle_app_config(app_config, **options)

Effectue les actions de la commande pour app_config, qui sera une instance de AppConfig correspondant à une étiquette d’application passée via la ligne de commande.

Changed in Django 1.7:

Auparavant, les sous-classes de AppCommand devaient implémenter handle_app(app, **options)app était un module de modèles. La nouvelle API permet de gérer des applications sans module de modèles. Le moyen le plus rapide de migrer est comme suit :

def handle_app_config(app_config, **options):
    if app_config.models_module is None:
        return                                  # Or raise an exception.
    app = app_config.models_module
    # Copy the implementation of handle_app(app_config, **options) here.

Cependant, vous pourriez être en mesure de simplifier l’implémentation en utilisant directement les attributs de app_config.

class LabelCommand

Une commande de gestion qui accepte un ou plusieurs paramètres arbitraires (labels) en ligne de commande et effectue une opération pour chacun d’eux.

Plutôt que d’implémenter handle(), les sous-classes doivent implémenter handle_label() qui sera appelée une fois pour chaque paramètre (label).

LabelCommand.handle_label(label, **options)

Effectue les actions de la commande pour label, qui sera la chaîne telle que passée à la ligne de commande.

class NoArgsCommand

Obsolète depuis la version 1.8: Utilisez BaseCommand à la place, qui n’accepte aucun paramètre par défaut.

Une commande qui n’accepte aucun paramètre en ligne de commande.

Plutôt que d’implémenter handle(), les sous-classes doivent implémenter handle_noargs() ; handle() est elle-même surchargée pour s’assurer qu’aucun paramètre n’est passé à la ligne de commande.

NoArgsCommand.handle_noargs(**options)

Effectue les actions de cette commande.

Exceptions des commandes

class CommandError

Classe d’exception indiquant qu’un problème est survenu lors de l’exécution d’une commande de gestion.

Si cette exception est levée durant l’exécution d’une commande de gestion à partir de la console en ligne de commande, elle sera interceptée et transformée en un message d’erreur proprement affiché à destination du flux de sortie approprié (par ex. stderr). En conséquence, le déclenchement de cette exception (accompagné d’une description significative de l’erreur) est la manière privilégiée de signaler que quelque chose s’est mal passé durant l’exécution d’une commande.

Si une commande de gestion est appelée depuis du code par l’intermédiaire de call_command, c’est alors à vous d’intercepter cette exception si nécessaire.

Back to Top