Cómo crear comandos personalizados de django-admin

Las aplicaciones pueden registrar sus propias acciones con manage.py. Por ejemplo, podrías querer agregar una acción para manage.py en una aplicación Django que estás distribuyendo. En este documento, vamos a construir un comando personalizado closepoll para la aplicación polls de la tutorial.

Para hacer esto, agrega un directorio management/commands a la aplicación. Django registrará un comando para manage.py para cada módulo Python en ese directorio cuyo nombre no comienza con un guión bajo. Por ejemplo:

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

En este ejemplo, el comando closepoll estará disponible en cualquier proyecto que incluya la aplicación polls en INSTALLED_APPS.

El módulo _private.py no estará disponible como un comando de administración.

El módulo closepoll.py tiene una sola exigencia: debe definir una clase Command que extienda BaseCommand o uno de sus subclases.

Scripts independientes

Los comandos de administración personalizados son especialmente útiles para ejecutar scripts independientes o scripts que se ejecutan periódicamente desde el crontab UNIX o desde la consola de tareas programadas de Windows.

Para implementar el comando, edita polls/management/commands/closepoll.py para que tenga este aspecto:

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)
            )

Nota

Cuando estés utilizando comandos de administración y desees proporcionar salida en la consola, debes escribir en self.stdout y self.stderr, en lugar de imprimir directamente a stdout y stderr. Al utilizar estos proxies, se vuelve mucho más fácil probar tu comando personalizado. Ten en cuenta también que no necesitas agregar un carácter de nueva línea al final de las mensajes, ya que se agregará automáticamente, a menos que especifiques el parámetro ending:

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

La nueva ordenanza personalizada se puede llamar utilizando python manage.py closepoll <poll_ids>.

El método handle() acepta uno o más poll_ids y establece poll.opened en False para cada uno de ellos. Si el usuario hace referencia a alguna encuesta inexistente, se levanta un CommandError. El atributo poll.opened no existe en la tutorial y se agregó a polls.models.Question para este ejemplo.

Aceptar argumentos opcionales

La misma ordenanza closepoll podría modificarse fácilmente para eliminar una encuesta dada en lugar de cerrarla aceptando opciones adicionales de línea de comandos. Estas opciones personalizadas se pueden agregar en el método add_arguments() como se muestra a continuación:

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

La opción (delete en nuestro ejemplo) está disponible en el diccionario de opciones del parámetro handle método. Consulte la documentación Python de argparse para obtener más información sobre el uso de add_argument.

Además de poder agregar opciones de línea de comandos personalizadas, todas las órdenes de administración pueden aceptar algunas opciones predeterminadas como --verbosity y --traceback.

Órdenes de administración y locales

Por defecto, las órdenes de administración se ejecutan con el locale activo actual.

Si por alguna razón tu ordenanza personalizada debe ejecutarse sin un locale activo (por ejemplo, para evitar que el contenido traducido se inserte en la base de datos), desactiva las traducciones utilizando el decorador @no_translations en tu método handle():

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


class Command(BaseCommand):
    ...

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

Dado que la desactivación de traducciones requiere acceso a configuraciones configuradas, el decorador no se puede utilizar para órdenes que funcionan sin configuraciones configuradas.

Testing

La información sobre cómo probar comandos de administración personalizados se puede encontrar en los docs de prueba.

Sobrescribiendo comandos

Django registra los comandos integrados y luego busca comandos en INSTALLED_APPS en orden inverso. Durante la búsqueda, si un nombre de comando duplica a un comando ya registrado, el comando recién descubierto sobrescribe al primero.

En otras palabras, para sobrescribir un comando, el nuevo comando debe tener el mismo nombre y su aplicación debe estar antes de la aplicación del comando sobrescrito en INSTALLED_APPS.

Los comandos de administración de aplicaciones de terceros que han sido sobrescritos por error pueden hacerse disponibles bajo un nuevo nombre creando un nuevo comando en una de las aplicaciones de tu proyecto (ordenada antes de la aplicación de terceros en INSTALLED_APPS) que importe el Command del comando sobrescrito.

Objetos de comandos

class BaseCommand[fuente]

La clase base desde la cual todos los comandos de administración derivan finalmente.

Utiliza esta clase si quieres acceder a todas las mecánicas que parsean los argumentos de línea de comandos y determinan qué código llamar en respuesta; si no necesitas cambiar ese comportamiento, considera utilizar uno de sus subclases.

Sobrescribir la clase BaseCommand requiere que implementes el método handle().

Atributos

Todos los atributos pueden establecerse en tu clase derivada y se pueden utilizar en las clases BaseCommand’s de <ref-basecommand-subclasses> referencias.

BaseCommand.help

Una descripción breve del comando, que se imprimirá en el mensaje de ayuda cuando el usuario ejecuta el comando python manage.py help <comando>.

BaseCommand.missing_args_message

Si tu comando define argumentos posicionales obligatorios, puedes personalizar el mensaje de error devuelto en caso de faltan argumentos. El valor por defecto es el devuelto por argparse («too few arguments»).

BaseCommand.output_transaction

Un booleano que indica si el comando imprime sentencias SQL; si True, el resultado se envolverá automáticamente con BEGIN; y COMMIT;. El valor por defecto es False.

BaseCommand.requires_migrations_checks

Un booleano; si True, el comando imprime una advertencia si la lista de migraciones en disco no coincide con las migraciones en la base de datos. Una advertencia no impide que el comando se ejecute. El valor por defecto es False.

BaseCommand.requires_system_checks

Una lista o tupla de etiquetas, por ejemplo [Tags.staticfiles, Tags.models]. Se comprobarán los controles del sistema registrados en las etiquetas elegidas antes de ejecutar el comando. El valor '__all__' se puede utilizar para especificar que todos los controles del sistema deben realizarse. El valor por defecto es '__all__'.

BaseCommand.style

Un atributo de instancia que ayuda a crear salida coloreada al escribir en stdout o stderr. Por ejemplo:

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

Consulte Coloración sintáctica para aprender a modificar el paleta de colores y ver los estilos disponibles (utiliza versiones mayúsculas de las «roles» descritas en esa sección).

Si pasas la opción --no-color al ejecutar tu comando, todas las llamadas a self.style() devolverán la cadena original sin colorear.

BaseCommand.suppressed_base_arguments

Los textos traducidos son:

Métodos

La clase BaseCommand tiene unos pocos métodos que pueden sobreescribirse pero solo el método handle() debe implementarse.

Implementación de un constructor en una subclase

Si implementas __init__ en tu subclase de BaseCommand, debes llamar al __init__ de BaseCommand:

class Command(BaseCommand):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # ...
BaseCommand.create_parser(prog_name, subcommand, **kwargs)[fuente]

Devuelve una instancia de CommandParser, que es una subclase de ArgumentParser con algunas personalizaciones para Django.

Puedes personalizar la instancia sobrescribiendo este método y llamando a super() con los parámetros kwargs de ArgumentParser.

BaseCommand.add_arguments(parser)[fuente]

Punto de entrada para agregar argumentos del parser a manejar los argumentos de línea de comandos pasados al comando. Las comandas personalizadas deben sobreescribir este método para agregar tanto argumentos posicionales como opcionales aceptados por el comando. No es necesario llamar a super() cuando se hereda directamente de BaseCommand.

BaseCommand.get_version()[fuente]

Devuelve la versión de Django, que debe ser correcta para todas las comandas integradas en Django. Las comandas proporcionadas por el usuario pueden sobreescribir este método para devolver su propia versión.

BaseCommand.execute(*args, **options)[fuente]

Intenta ejecutar este comando, realizando comprobaciones del sistema si es necesario (como se controla mediante la propiedad requires_system_checks). Si el comando lanza una excepción CommandError, se intercepta y se imprime en stderr.

Llamar a una orden de gestión en tu código

No debes llamar directamente a execute() desde tu código para ejecutar una orden. Utiliza call_command() en su lugar.

BaseCommand.handle(*args, **options)[fuente]

La lógica real de la orden. Las subclases deben implementar este método.

Puede devolver una cadena que se imprimirá en stdout (envuelta por BEGIN; y COMMIT; si output_transaction es True).

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False, include_deployment_checks=False, fail_level=checks.ERROR, databases=None)[fuente]

Utiliza el marco de trabajo de comprobación del sistema para inspeccionar todo el proyecto Django para problemas potenciales. Los problemas serios se elevan como una CommandError; las advertencias se imprimen en stderr; las notificaciones menores se imprimen en stdout.

Si app_configs y tags son ambos None, se realizan todas las comprobaciones del sistema excepto las relacionadas con la depuración y la base de datos. tags puede ser una lista de etiquetas de comprobación, como compatibilidad o modelos.

Puedes pasar include_deployment_checks=True para realizar también las comprobaciones de depuración, y una lista de alias de bases de datos en la databases para ejecutar comprobaciones relacionadas con la base de datos contra ellas.

BaseCommand.get_check_kwargs(options)[fuente]

Proporciona kwargs para la llamada a check(), incluyendo la transformación del valor de requires_system_checks al kwarg tag.

Sobreescribe este método para cambiar los valores suministrados a check(). Por ejemplo, para optar por las comprobaciones relacionadas con la base de datos puedes sobrescribir get_check_kwargs() de la siguiente manera:

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

Subclases de BaseCommand

class AppCommand

Un comando de gestión que toma uno o más etiquetas de aplicaciones instaladas como argumentos y hace algo con cada una de ellas.

En lugar de implementar el método handle(), las subclases deben implementar el método handle_app_config(), que se llamará una vez para cada aplicación.

AppCommand.handle_app_config(app_config, **options)

Realiza las acciones del comando para app_config, que será un objeto AppConfig correspondiente a la etiqueta de aplicación dada en la línea de comandos.

class LabelCommand

Un comando de gestión que toma uno o más argumentos arbitrarios (etiquetas) en la línea de comandos y hace algo con cada una de ellas.

En lugar de implementar el método handle(), las subclases deben implementar el método handle_label(), que se llamará una vez para cada etiqueta.

LabelCommand.label

Una cadena que describe los argumentos arbitrarios pasados al comando. La cadena se utiliza en el texto de uso y mensajes de error del comando. Por defecto es 'label'.

LabelCommand.handle_label(label, **options)

Realiza las acciones del comando para label, que será la cadena dada en la línea de comandos.

Excepciones de comandos

exception CommandError(returncode=1)[fuente]

Clase de excepción que indica un problema al ejecutar un comando de gestión.

Si esta excepción se levanta durante la ejecución de un comando de gestión desde una consola de línea de comandos, se capturará y convertirá en un mensaje de error bien formateado para el flujo de salida apropiado (es decir, stderr); como resultado, levantar esta excepción (con una descripción sensata del error) es la forma preferida de indicar que algo ha ido mal al ejecutar un comando. Acepta el argumento opcional returncode para personalizar el estado de salida para que el comando salga con él, utilizando sys.exit().

Si una orden de administración se llama desde el código a través de call_command(), es tu responsabilidad atrapar la excepción cuando sea necesario.