# Documentation Django 6.0.7

# É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 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

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 Question as Poll

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

        def add_arguments(self, parser):
            parser.add_argument("poll_ids", nargs="+", type=int)

        def handle(self, *args, **options):
            for poll_id in options["poll_ids"]:
                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(
                    self.style.SUCCESS('Successfully closed poll "%s"' % poll_id)
                )

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_ids>`.

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.Question` 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_ids", nargs="+", type=int)

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

        def handle(self, *args, **options):
            # ...
            if options["delete"]:
                poll.delete()
            # ...

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, les commandes d’administration sont exécutées avec la langue active.

Si pour une raison quelconque, votre propre commande d’administration doit fonctionner sans langue active (par exemple pour empêcher du contenu traduit d’être inséré dans la base de données), désactivez toute traduction en appliquant le décorateur `@no_translations` à votre méthode `handle()`:

    from django.core.management.base import BaseCommand, no_translations

    class Command(BaseCommand):
        ...

        @no_translations
        def handle(self, *args, **options): ...

Comme la désactivation des traductions nécessite d’accéder aux réglages configurés, ce décorateur ne peut pas être utilisé pour des commandes qui doivent fonctionner même si les réglages ne sont pas configurés.

## 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.

## Surcharge des commandes

Django enregistre les commandes intégrées et recherche ensuite les commandes dans l’ordre inverse de `INSTALLED_APPS`. Pendant la recherche, si le nom d’une commande est un duplicata d’une commande déjà enregistrée, la commande nouvellement découverte surcharge la première.

En d’autres termes, pour surcharger une commande, la nouvelle commande doit avoir le même nom et son application doit se trouver avant l’application de la commande dont on souhaite la surcharge dans `INSTALLED_APPS`.

Les commandes de gestions des applications tierces qui ont été involontairement surchargées peuvent être mises à disposition sous un nouveau nom en créant une nouvelle commande dans une des applications du projet (placée avant l’application tierce dans `INSTALLED_APPS`) qui importe la `Command` de la commande surchargée.

## 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.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

  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.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_migrations_checks

  Valeur booléenne ; si `True`, la commande affiche un avertissement si le jeu de migrations sur disque ne correspond pas aux migrations dans la base de données. Un avertissement n’empêche pas la commande de s’exécuter. La valeur par défaut est `False`.

BaseCommand.requires_system_checks

  Une liste ou un tuple d’étiquettes, par ex. `[Tags.staticfiles, Tags.models]`. Les contrôles système inscrits avec les étiquettes indiquées seront exécutés avant d’exécuter la commande. La valeur `'__all__'` peut être utilisée pour indiquer que tous les contrôles système doivent être appliqués. La valeur par défaut est `'__all__'`.

BaseCommand.style

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

      self.stdout.write(self.style.SUCCESS("..."))

  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.

BaseCommand.suppressed_base_arguments

  Les options de commande à supprimer par défaut dans l’affichage de l’aide. Cela doit être un ensemble de noms d’options (par ex. `'--verbosity'`). Les valeurs par défaut des options supprimées sont tout de même transmises.

### 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().__init__(*args, **kwargs)
            # ...

BaseCommand.create_parser(prog_name, subcommand, **kwargs)

  Renvoie une instance `CommandParser`, elle-même une sous-classe de `ArgumentParser` avec quelques adaptations pour Django.

  Vous pouvez personnaliser l’instance en surchargeant cette méthode et en appelant `super()` avec les paramètres `kwargs` de `ArgumentParser`.

BaseCommand.add_arguments(parser)

  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 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, include_deployment_checks=False, fail_level=checks.ERROR, databases=None)

  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, à l’exception des contrôles liés au déploiement et à la base de données. `tags` peut être une liste d’étiquettes de contrôle, comme `compatibility` ou `models`.

  Vous pouvez transmettre `include_deployment_checks=True` pour lancer en plus les contrôles de déploiement, ainsi que fournir une liste d’alias de base de données dans le paramètre `databases` pour lancer les contrôles liés aux bases de données sur ces bases.

BaseCommand.get_check_kwargs(options)

  Fournit des paramètres nommés pour l’appel à `check()`, ce qui inclut la transformation de la valeur de `requires_system_checks` au paramètre `tag`.

  Surchargez cette méthode pour modifier les valeurs fournies à `check()`. Par exemple, pour forcer le choix des contrôles liés aux bases de données, Vous pouvez surcharger `get_check_kwargs()` comme suit :

      def get_check_kwargs(self, options):
          kwargs = super().get_check_kwargs(options)
          return {**kwargs, "databases": [options["database"]]}

### 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.

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.label

  Une chaîne indiquant les paramètres arbitraires transmis à la commande. La chaîne est utilisée dans le texte d’aide et dans les messages d’erreur de la commande. Contient `label` par défaut.

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.

### Exceptions des commandes

exception CommandError(returncode=1)

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. Elle accepte le paramètre facultatif `returncode` pour personnaliser l’état de sortie de la commande d’administration, en utilisant `sys.exit()`.

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.