Come creare comandi django-admin personalizzati

Le applicazioni possono registrare le proprie azioni con manage.py. Per esempio, potresti voler aggiungere una azione manage.py per una applicazione Django che stai distribuendo. In questo documento, costruiremo un comando personalizzato closepoll per l’applicazione polls dal tutorial.

To do this, add a management/commands directory to the application. Django will register a manage.py command for each Python module in that directory whose name doesn’t begin with an underscore. For example:

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

In questo esempio, il comando closepoll sarà reso disponibile a qualsiasi progetto che includa l’applicazione polls in INSTALLED_APPS.

Il modulo _private.py non sarà disponibile come comando di management.

Il modulo closepoll.py ha un solo requisito – deve definire una classe Command che estenda BaseCommand o una delle sue sottoclassi.

Script standalone

Il comandi di management personalizzati sono utili soprattutto per lanciare script standalone o per script che sono eseguiti periodicamente con il crontab di UNIX o dal pannello di controllo per la pianificazione dei task di Windows.

Per implementare il comando, modifica polls/management/commands/closepoll.py in questo modo :

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

Quando utilizzi i comandi di management personalizzati e vuoi ottenere dell’output su console, dovresti scrivere su self.stdout e self.stderr, invece che direttamente su stdout` and stderr. Utilizzando questi proxy, diventa molto più facile testare il tuo comando personalizzato. Nota anache che non hai bisogno di terminare i messaggi con il carattere di newline, sarà aggiunto automaticamente, a meno che tu non specifichi il parametro ending:

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

Il nuovo comando personalizzato potrà essere chiamato con python manage.py closepoll <poll_ids>.

Il metodo handle() prende uno o più poll_ids ed imposta poll.opened a False per ognuno di essi. Se l’utente fa riferimento ad un poll non esistente, viene sollevato un CommandError. L’attributo poll.opened non esiste nel tutorial ed è stato aggiunto a polls.models.Question per questo esempio.

Accetta argomenti opzionali

Lo stesso closepoll  può essere modificato facilmente per cancellare un dato poll invece che chiuderlo accettando opzioni sulla command line. Queste opzioni personalizzate possono essere aggiunte nel metodo add_arguments() così:

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’opzione (delete nel nostro esempio) è disponibile nel dizionario di parametri delle opzioni del metodo handle. Vedi la documentazione di Python di argparse per saperne di più sull’utilizzo di add_argument.

Oltre ad essere in grado di aggiungere opzioni personalizzate per la command line, tutti i comandi di management possono accettare alcune opzioni di default come --verbosity e --traceback.

Commandi di gestione e traduzioni/locale

Di default, i comandi di management sono eseguiti con il locale che è correntemente attivo.

Se, per qualche ragione, il tuo comando di management personalizzato deve girare senza un locale attivo (per esempio, per evitare che il contenuto tradotto venga inserito nel database), disattiva le traduzioni usando il decoratore @no_translations  sul tuo metodo handle()

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


class Command(BaseCommand):
    ...

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

Dal momento che la disattivazione della traduzione richiede l’accesso alle impostazioni di configurazione, il decoratore non può essere usato per comandi che funzionino senza impostazioni di configurazione.

Testing

Informazioni su come testare i comandi di management personalizzati possono essere trovate nei documenti di testing.

Override dei comandi

Django registra i comandi built-in e poi cerca comandi in INSTALLED_APPS in senso inverso. Durante la ricerca, se un nome di comando duplica un comando già registrato, fa override su quello precedente.

In altre parole, per fare override di un comando, il nuovo comando deve avere lo stesso nome e la sua app deve trovarsi prima di quella del comando su cui fare override in INSTALLED_APPS.

I comandi di management di app di terze parti che sono stati sovrascritti in modo non intenzionale posso essere resi disponibili con un nuovo nome creando un nuovo comando in una delle app del tuo progetto (che si trovi in ordine prima dell’app di terze parti in INSTALLED_APPS) che importi il Command del comando sovrascritto.

Oggetti Command

class BaseCommand

La classe di base da cui, in ultima analisi, tutti i comandi di management derivano.

Usa questa classe se vuoi accedere a tutti i meccanismi che fanno parsing degli argomenti di command-line e cercano di capire quale codice chiamare in risposta; se non hai bisogno di cambiare questo comportamento, considera di usare una delle sue sottoclassi.

Fare una sottoclasse della classe BaseCommand ti richiede di implementare il metodo handle().

Attributi

Tutti gli attributi possono essere impostati nella tua classe derivata e possono essere usati nelle sottoclassi di BaseCommand.

BaseCommand.help

Una breve descrizione del comando, che sarà stampata nel messaggio di aiuto quando l’utente lancia il comando python manage.py help <command>.

BaseCommand.missing_args_message

Se il tuo comando definisce degli argomenti posizionali obbligatori, puoi personalizzare il messaggio di errore restituito in caso di argomenti mancanti. Quello di default è restituito da argparse («too few arguments»).

BaseCommand.output_transaction

Un booleano che indica se il comando mostra in output uno statement SQL; se True, l’output sarà automaticamente inserito tra BEGIN; e COMMIT;. Il valore predefinito è False.

BaseCommand.requires_migrations_checks

Un booleano; se True, il comando stampa un messaggio di warning se il set di migrazioni su disco non corrisponde a quelle sul database. Un warning non impedisce l’esecuzione del comando. Il valore predefinito è False.

BaseCommand.requires_system_checks

Un lista di tuple di tag, per esempio [Tags.staticfiles, Tags.models]. Il sistema controlla che la registrazione nei tag scelti sia sottoposta a controllo di errori prima di eseguire il comando. Il valore '__all__' può essere usato per specificare che tutti i controlli siano eseguiti. Il valore predefinito è '__all__'.

BaseCommand.style

Un attributo di istanza che aiuta a creare output colorato quando si scrive su stdout o stderr. Per esempio:

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

Vedi Syntax coloring per imparare come modificare la palette di colori e visualizzare gli stili disponibili (usa le versioni maiuscole dei «ruoli» descritti in quella sezione).

Se passi l’opzione --no-color quando lanci il comando, tutte le chiamate self.style() restituiranno la stringa originale senza colori.

BaseCommand.suppressed_base_arguments

Le opzioni dei comandi di default da sopprimere nell’output dell’help. Dovrebbe essere un set di nomi di opzione (per es. '--verbosity'). I valori di default per tutte le opzioni soppresse vengono comunque passati.

Metodi

La classe BaseCommand ha pochi metodi che possono essere sovrascritti ma solo il metodo handle() deve essere implementato.

Implementazione di un constructor in una subclasse

Se implementi __init__ nella tua sottoclasse di BaseCommand, devi chiamare __init__ di BaseCommand:

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

Restituisce una istanza di CommandParser, che è una sottoclasse di ArgumentParser con qualche personalizzazione per Django.

Puoi personalizzare l’istanza sovrascrivendo questo metodo e chiamando super() con parametri kwargs ArgumentParser.

BaseCommand.add_arguments(parser)

L’entry point per aggiungere argomenti del parser per gestire gli argomenti della command line passati al comando. I comandi personalizzati dovrebbero sovrascrivere questo metodo per aggiungere sia argomenti facoltativi che posizionali che siano accettati dal comando. Non è necessario chiamare super() quando si utilizza una sottoclasse di BaseCommand.

BaseCommand.get_version()

Restituisce la versione di Django, che dovrebbe essere corretta per tutti i comandi Django built-in. I comandi forniti dall’utente possono sovrascrivere questo metodo per restituire la propria versione.

BaseCommand.execute(*args, **options)

Cerca di eseguire questo comando, facendo i controlli di sistema se necessario (come controllato con l’attributo requires_system_checks). Se il comando solleva CommandError, questo viene intercettato e stampato su stderr.

Chiamata di un commando di ?gestione? nel tuo codice

execute()` non dovrebbe essere chiamato direttamente dal tuo codice per eseguire un comando. Usa invece call_command().

BaseCommand.handle(*args, **options)

La logica attuale del comando. Le sottoclassi devono implementare questo metodo

Può restituire una stringa che sarà stampata su stdout (tra BEGIN; e COMMIT; se output_transaction è True).

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)

Usa il framework di controlli di sistema per ispezionare l’intero progetto Django in cerca di potenziali problemi. I problemi gravi sono segnalati cpme CommandError; i warning sono stampati su stderr; le notifiche minori sono stampate su stdout.

Se app_configs e tags sono entrambi None, sono effettuati tutti i controlli di sistema. tags può essere una lista di tag di controllo, come compatibility o models.

sottoclassi BaseCommand

class AppCommand

Un comando di gestione che accetta una o più etichette di applicazioni installate come argomenti e fa qualcosa con ciascuna di esse.

Piuttosto che implementare handle(), le sottoclassi devono implementare handle_app_config(), che sarà chiamata una volta per ogni applicazione.

AppCommand.handle_app_config(app_config, **options)

Effettua le azioni del comando per app_config, che sarà una istanza di AppConfig corrispondente ad una etichetta di applicazione fornita sulla linea di comando.

class LabelCommand

Un comando di gestione che accetta uno o più argomenti arbitrari (etichette) dalla linea di comando e fa qualcosa con ciascuno di essi.

Piuttosto che implementare handle(), le sottoclassi devono implmentare handle_label(), che sarà chiamato una volta per ogni etichetta.

LabelCommand.label

Una stringa che descrive gli argomenti arbitrari forniti al comando. La stringa viene usata nel testo di utilizzo e nei messaggi di errore del comando. Di default è pari a 'label'.

LabelCommand.handle_label(label, **options)

Effettua le azioni del comando per label, che sarà la stringa come è fornita dalla riga di comando.

Eccezioni di comando

exception CommandError(returncode=1)

Una classe di eccezione che indica un problema mentre si esegue un comando di gestione.

Se questa eccezione viene sollevata durante l’esecuzione di un comando di management da una console a linea di comando, sarà gestito e trasformato in un messaggio di errore stampato in modo gradevole verso lo stream di output appropriato (per es. stderr); di conseguenza, sollevare questa eccezione (con una descrizione sensibile dell’errore) è la modalità preferita di indicare che qualcosa è andato storto durante l’esecuzione di un comando. Essa accetta l’argomento opzionale returncode per personalizzare l’exit status del comando di management, usando sys.exit().

Se il comando di gestione è chiamato dal codice con call_command(), è tuo compito catturare l’eccezione quando è necessario.

Back to Top