Creating management commands in Django

Management commands - commands from the command line using the manage.py script.

The most frequent areas of application are actions performed one-time or periodically, but for which, for some reason, launch through the scheduler is not available. For example, sending users one-time messages, retrieving a selection of data from the database, checking the availability of the necessary files and folders before rolling updates, quickly creating model objects during development, etc.

The basics


Commands are created in the application directories (applications must be added to INSTALED_APPS), in the app / management / commands subdirectory. A separate file is created for each command. Files other than those beginning with an underscore will be available from the command line.

app/
    __init__.py
    management/
        __init__.py
        commands/
            __init__.py
            _tools.py
            zen.py

In this example, the zen command will be available, but _tools will not.

Commands are created by inheriting from the django.core.management.base.BaseCommand class. In the simplest case, it suffices to rewrite the function handle. This command will be executed, and the result returned by it will be printed on the console.

from django.core.management.base import BaseCommand
classCommand(BaseCommand):
    help = 'The Zen of Python'defhandle(self, *args, **options):import this

Let's try to call our team:

python manage.py zen
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
...

The attribute of the help class is a description that is displayed when calling a command with the key --help or if the command is entered incorrectly.

$ python manage.py zen --helpusage: manage.py zen [-h] [--version] [-v {0,1,2,3}] [--settings SETTINGS]
                     [--pythonpath PYTHONPATH] [--traceback] [--no-color]TheZenofPythonoptional arguments:
  -h, --help            show this help message and exit--version             show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}Verbosity level; 0=minimal output, 1=normal output,
                        2=verbose output, 3=very verbose output
  --settings SETTINGS   The Python path to a settings module, e.g."myproject.settings.main". If this isn't provided, the
                        DJANGO_SETTINGS_MODULE environment variable will be
                        used.
  --pythonpath PYTHONPATHA directory to add to the Python path, e.g.
                        "/home/djangoprojects/myproject".
  --traceback           Raise on CommandError exceptions--no-color            Don't colorize the command output.

Argument parsing


As we can see, our team already takes 7 arguments.

But what if this is not enough for us and we want to add our options? To do this, we need to create the function add_arguments in our class, where we list all the arguments that we want to pass to our team. Each argument is created by calling the parser.add_argument function with a number of parameters.

Suppose we are not satisfied with the long output of our function and we want the -s and --short keys to print just Hello world:

from django.core.management.base import BaseCommand
classCommand(BaseCommand):
    help = 'The Zen of Python'defhandle(self, *args, **options):if options['short']:
            import __hello__
        else:
            import this
    defadd_arguments(self, parser):
        parser.add_argument(
        '-s', 
        '--short',
        action='store_true', 
        default=False,
        help='Вывод короткого сообщения'
        )

This file when called with the -s option will print Hello world.

$ python manage.py zen -s 
Hello world...
(coolwriter)$ python manage.py zen
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
...

Let's take a closer look at how argument parsing is set.

The parser is inherited from argparse.ArgumentParser, and the arguments taken by the add_argument function can be found in the documentation for the argparse library in the python documentation. The most important are:

name or flags - the name or list of names and labels. We have this 'short', '-s' and '--short'.
action - what to do with the argument value. The most interesting (but not all) options are as follows:

store — save as the value of our option. If several key-value pairs are passed, the value of the last
store_const is saved — save the constant value in the name key. The constant is passed as an argument to const of the same function add_argument.

For example:

parser.add_argument('my_option', action='store_const', const=1234)

The handle functions in options will be passed to 'my_option': 1234

store_true, store_false - special cases of the previous option. True or False
append is saved - the value will append to the end of the
append_const list - the same as the previous one, but the value passed to the const argument (which can be any object)

nargs is the number of arguments. Possible values: integer, '?' - one or the default value from the default argument, '*' - all as many as there are, and are collected into the list, '+' - at least one, and if several are assembled into the list, argparse.REMAINDER - all remaining arguments from the command line are collected there . Incompatible with action = const

Note:if you use this argument, the value of your option from the command line will be transferred to the handle as a list, even if there is only one element. (At the same time, the default value is transmitted as is, without reduction to the list.) Default is the

default value.
type - casting the argument to the specified type.
choices - limiting the options for the argument value. The value of choices is passed a list of possible values ​​of
required - a required argument.
help - A description of what this argument does.
dest - if you want to save your option under a different name, you can specify dest = 'my_new_name'. Otherwise, the name of the argument will be used.
These arguments will be further passed to the handle function in the form of the options dictionary.

But what if we want to pass unnamed arguments to handle?

In this case, add_arguments to get all unnamed arguments in the args option. For example, if we want to pass several integers to a command:

defadd_arguments(self, parser):
parser.add_argument(
            nargs='+',
            type=int,
            dest = 'args'
        )

Execution order


The command is executed in the following order:

  1. First, the run_from_argv () method is called. This method creates a parser through a call to create_parser, and the created parser adds default arguments (of type no-color), and also calls the add_arguments method, thus adding all the options we created.
  2. After that, the execute function is called. This function performs several checks, and then runs the function handle. The result of the execution of handle is printed to standard output.

Of course, any of these functions can be customized. For example, let's make a nice multi-line help message:

from argparse import RawTextHelpFormatter
    defcreate_parser(self, prog_name, subcommand):
        parser = super(Command, self).create_parser(prog_name, subcommand)
        parser.formatter_class = RawTextHelpFormatter
        return parser

Here, perhaps, is all that is needed for writing management commands in most cases.

Conclusion


The article does not claim to be a complete overview of all the possibilities for creating management commands - they are described in the Django documentation. Those interested can refer to the
documentation .

Parsing command line arguments there, alas, is practically not disclosed. Those wishing to immerse themselves in the subtleties of this issue should examine the python documentation.

Argparse module

Also popular now: