mirror of https://github.com/django/django.git
379 lines
14 KiB
Plaintext
379 lines
14 KiB
Plaintext
==============================================
|
||
How to create custom ``django-admin`` commands
|
||
==============================================
|
||
|
||
.. module:: django.core.management
|
||
|
||
Applications can register their own actions with ``manage.py``. For example,
|
||
you might want to add a ``manage.py`` action for a Django app that you're
|
||
distributing. In this document, we will be building a custom ``closepoll``
|
||
command for the ``polls`` application from the
|
||
:doc:`tutorial</intro/tutorial01>`.
|
||
|
||
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:
|
||
|
||
.. code-block:: text
|
||
|
||
polls/
|
||
__init__.py
|
||
models.py
|
||
management/
|
||
__init__.py
|
||
commands/
|
||
__init__.py
|
||
_private.py
|
||
closepoll.py
|
||
tests.py
|
||
views.py
|
||
|
||
In this example, the ``closepoll`` command will be made available to any project
|
||
that includes the ``polls`` application in :setting:`INSTALLED_APPS`.
|
||
|
||
The ``_private.py`` module will not be available as a management command.
|
||
|
||
The ``closepoll.py`` module has only one requirement -- it must define a class
|
||
``Command`` that extends :class:`BaseCommand` or one of its
|
||
:ref:`subclasses<ref-basecommand-subclasses>`.
|
||
|
||
.. admonition:: Standalone scripts
|
||
|
||
Custom management commands are especially useful for running standalone
|
||
scripts or for scripts that are periodically executed from the UNIX crontab
|
||
or from Windows scheduled tasks control panel.
|
||
|
||
To implement the command, edit ``polls/management/commands/closepoll.py`` to
|
||
look like this::
|
||
|
||
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)
|
||
)
|
||
|
||
.. _management-commands-output:
|
||
|
||
.. note::
|
||
When you are using management commands and wish to provide console
|
||
output, you should write to ``self.stdout`` and ``self.stderr``,
|
||
instead of printing to ``stdout`` and ``stderr`` directly. By
|
||
using these proxies, it becomes much easier to test your custom
|
||
command. Note also that you don't need to end messages with a newline
|
||
character, it will be added automatically, unless you specify the ``ending``
|
||
parameter::
|
||
|
||
self.stdout.write("Unterminated line", ending="")
|
||
|
||
The new custom command can be called using ``python manage.py closepoll
|
||
<poll_ids>``.
|
||
|
||
The ``handle()`` method takes one or more ``poll_ids`` and sets ``poll.opened``
|
||
to ``False`` for each one. If the user referenced any nonexistent polls, a
|
||
:exc:`CommandError` is raised. The ``poll.opened`` attribute does not exist in
|
||
the :doc:`tutorial</intro/tutorial02>` and was added to
|
||
``polls.models.Question`` for this example.
|
||
|
||
.. _custom-commands-options:
|
||
|
||
Accepting optional arguments
|
||
============================
|
||
|
||
The same ``closepoll`` could be easily modified to delete a given poll instead
|
||
of closing it by accepting additional command line options. These custom
|
||
options can be added in the :meth:`~BaseCommand.add_arguments` method like this::
|
||
|
||
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()
|
||
# ...
|
||
|
||
The option (``delete`` in our example) is available in the options dict
|
||
parameter of the handle method. See the :py:mod:`argparse` Python documentation
|
||
for more about ``add_argument`` usage.
|
||
|
||
In addition to being able to add custom command line options, all
|
||
:doc:`management commands</ref/django-admin>` can accept some default options
|
||
such as :option:`--verbosity` and :option:`--traceback`.
|
||
|
||
.. _management-commands-and-locales:
|
||
|
||
Management commands and locales
|
||
===============================
|
||
|
||
By default, management commands are executed with the current active locale.
|
||
|
||
If, for some reason, your custom management command must run without an active
|
||
locale (for example, to prevent translated content from being inserted into
|
||
the database), deactivate translations using the ``@no_translations``
|
||
decorator on your :meth:`~BaseCommand.handle` method::
|
||
|
||
from django.core.management.base import BaseCommand, no_translations
|
||
|
||
|
||
class Command(BaseCommand):
|
||
...
|
||
|
||
@no_translations
|
||
def handle(self, *args, **options): ...
|
||
|
||
Since translation deactivation requires access to configured settings, the
|
||
decorator can't be used for commands that work without configured settings.
|
||
|
||
Testing
|
||
=======
|
||
|
||
Information on how to test custom management commands can be found in the
|
||
:ref:`testing docs <topics-testing-management-commands>`.
|
||
|
||
Overriding commands
|
||
===================
|
||
|
||
Django registers the built-in commands and then searches for commands in
|
||
:setting:`INSTALLED_APPS` in reverse. During the search, if a command name
|
||
duplicates an already registered command, the newly discovered command
|
||
overrides the first.
|
||
|
||
In other words, to override a command, the new command must have the same name
|
||
and its app must be before the overridden command's app in
|
||
:setting:`INSTALLED_APPS`.
|
||
|
||
Management commands from third-party apps that have been unintentionally
|
||
overridden can be made available under a new name by creating a new command in
|
||
one of your project's apps (ordered before the third-party app in
|
||
:setting:`INSTALLED_APPS`) which imports the ``Command`` of the overridden
|
||
command.
|
||
|
||
Command objects
|
||
===============
|
||
|
||
.. class:: BaseCommand
|
||
|
||
The base class from which all management commands ultimately derive.
|
||
|
||
Use this class if you want access to all of the mechanisms which
|
||
parse the command-line arguments and work out what code to call in
|
||
response; if you don't need to change any of that behavior,
|
||
consider using one of its :ref:`subclasses<ref-basecommand-subclasses>`.
|
||
|
||
Subclassing the :class:`BaseCommand` class requires that you implement the
|
||
:meth:`~BaseCommand.handle` method.
|
||
|
||
Attributes
|
||
----------
|
||
|
||
All attributes can be set in your derived class and can be used in
|
||
:class:`BaseCommand`’s :ref:`subclasses<ref-basecommand-subclasses>`.
|
||
|
||
.. attribute:: BaseCommand.help
|
||
|
||
A short description of the command, which will be printed in the
|
||
help message when the user runs the command
|
||
``python manage.py help <command>``.
|
||
|
||
.. attribute:: BaseCommand.missing_args_message
|
||
|
||
If your command defines mandatory positional arguments, you can customize
|
||
the message error returned in the case of missing arguments. The default is
|
||
output by :py:mod:`argparse` ("too few arguments").
|
||
|
||
.. attribute:: BaseCommand.output_transaction
|
||
|
||
A boolean indicating whether the command outputs SQL statements; if
|
||
``True``, the output will automatically be wrapped with ``BEGIN;`` and
|
||
``COMMIT;``. Default value is ``False``.
|
||
|
||
.. attribute:: BaseCommand.requires_migrations_checks
|
||
|
||
A boolean; if ``True``, the command prints a warning if the set of
|
||
migrations on disk don't match the migrations in the database. A warning
|
||
doesn't prevent the command from executing. Default value is ``False``.
|
||
|
||
.. attribute:: BaseCommand.requires_system_checks
|
||
|
||
A list or tuple of tags, e.g. ``[Tags.staticfiles, Tags.models]``. System
|
||
checks :ref:`registered in the chosen tags <registering-labeling-checks>`
|
||
will be checked for errors prior to executing the command. The value
|
||
``'__all__'`` can be used to specify that all system checks should be
|
||
performed. Default value is ``'__all__'``.
|
||
|
||
.. attribute:: BaseCommand.style
|
||
|
||
An instance attribute that helps create colored output when writing to
|
||
``stdout`` or ``stderr``. For example::
|
||
|
||
self.stdout.write(self.style.SUCCESS("..."))
|
||
|
||
See :ref:`syntax-coloring` to learn how to modify the color palette and to
|
||
see the available styles (use uppercased versions of the "roles" described
|
||
in that section).
|
||
|
||
If you pass the :option:`--no-color` option when running your command, all
|
||
``self.style()`` calls will return the original string uncolored.
|
||
|
||
.. attribute:: BaseCommand.suppressed_base_arguments
|
||
|
||
The default command options to suppress in the help output. This should be
|
||
a set of option names (e.g. ``'--verbosity'``). The default values for the
|
||
suppressed options are still passed.
|
||
|
||
Methods
|
||
-------
|
||
|
||
:class:`BaseCommand` has a few methods that can be overridden but only
|
||
the :meth:`~BaseCommand.handle` method must be implemented.
|
||
|
||
.. admonition:: Implementing a constructor in a subclass
|
||
|
||
If you implement ``__init__`` in your subclass of :class:`BaseCommand`,
|
||
you must call :class:`BaseCommand`’s ``__init__``::
|
||
|
||
class Command(BaseCommand):
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
# ...
|
||
|
||
.. method:: BaseCommand.create_parser(prog_name, subcommand, **kwargs)
|
||
|
||
Returns a ``CommandParser`` instance, which is an
|
||
:class:`~argparse.ArgumentParser` subclass with a few customizations for
|
||
Django.
|
||
|
||
You can customize the instance by overriding this method and calling
|
||
``super()`` with ``kwargs`` of :class:`~argparse.ArgumentParser` parameters.
|
||
|
||
.. method:: BaseCommand.add_arguments(parser)
|
||
|
||
Entry point to add parser arguments to handle command line arguments passed
|
||
to the command. Custom commands should override this method to add both
|
||
positional and optional arguments accepted by the command. Calling
|
||
``super()`` is not needed when directly subclassing ``BaseCommand``.
|
||
|
||
.. method:: BaseCommand.get_version()
|
||
|
||
Returns the Django version, which should be correct for all built-in Django
|
||
commands. User-supplied commands can override this method to return their
|
||
own version.
|
||
|
||
.. method:: BaseCommand.execute(*args, **options)
|
||
|
||
Tries to execute this command, performing system checks if needed (as
|
||
controlled by the :attr:`requires_system_checks` attribute). If the command
|
||
raises a :exc:`CommandError`, it's intercepted and printed to ``stderr``.
|
||
|
||
.. admonition:: Calling a management command in your code
|
||
|
||
``execute()`` should not be called directly from your code to execute a
|
||
command. Use :func:`~django.core.management.call_command` instead.
|
||
|
||
.. method:: BaseCommand.handle(*args, **options)
|
||
|
||
The actual logic of the command. Subclasses must implement this method.
|
||
|
||
It may return a string which will be printed to ``stdout`` (wrapped
|
||
by ``BEGIN;`` and ``COMMIT;`` if :attr:`output_transaction` is ``True``).
|
||
|
||
.. method:: BaseCommand.check(app_configs=None, tags=None,display_num_errors=False, include_deployment_checks=False, fail_level=checks.ERROR, databases=None)
|
||
|
||
Uses the system check framework to inspect the entire Django project for
|
||
potential problems. Serious problems are raised as a :exc:`CommandError`;
|
||
warnings are output to ``stderr``; minor notifications are output to
|
||
``stdout``.
|
||
|
||
If ``app_configs`` and ``tags`` are both ``None``, all system checks are
|
||
performed except deployment and database related checks. ``tags`` can be a
|
||
list of check tags, like ``compatibility`` or ``models``.
|
||
|
||
You can pass ``include_deployment_checks=True`` to also perform deployment
|
||
checks, and list of database aliases in the ``databases`` to run database
|
||
related checks against them.
|
||
|
||
.. _ref-basecommand-subclasses:
|
||
|
||
``BaseCommand`` subclasses
|
||
--------------------------
|
||
|
||
.. class:: AppCommand
|
||
|
||
A management command which takes one or more installed application labels as
|
||
arguments, and does something with each of them.
|
||
|
||
Rather than implementing :meth:`~BaseCommand.handle`, subclasses must
|
||
implement :meth:`~AppCommand.handle_app_config`, which will be called once for
|
||
each application.
|
||
|
||
.. method:: AppCommand.handle_app_config(app_config, **options)
|
||
|
||
Perform the command's actions for ``app_config``, which will be an
|
||
:class:`~django.apps.AppConfig` instance corresponding to an application
|
||
label given on the command line.
|
||
|
||
.. class:: LabelCommand
|
||
|
||
A management command which takes one or more arbitrary arguments (labels) on
|
||
the command line, and does something with each of them.
|
||
|
||
Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
|
||
:meth:`~LabelCommand.handle_label`, which will be called once for each label.
|
||
|
||
.. attribute:: LabelCommand.label
|
||
|
||
A string describing the arbitrary arguments passed to the command. The
|
||
string is used in the usage text and error messages of the command.
|
||
Defaults to ``'label'``.
|
||
|
||
.. method:: LabelCommand.handle_label(label, **options)
|
||
|
||
Perform the command's actions for ``label``, which will be the string as
|
||
given on the command line.
|
||
|
||
Command exceptions
|
||
------------------
|
||
|
||
.. exception:: CommandError(returncode=1)
|
||
|
||
Exception class indicating a problem while executing a management command.
|
||
|
||
If this exception is raised during the execution of a management command from a
|
||
command line console, it will be caught and turned into a nicely-printed error
|
||
message to the appropriate output stream (i.e., ``stderr``); as a result,
|
||
raising this exception (with a sensible description of the error) is the
|
||
preferred way to indicate that something has gone wrong in the execution of a
|
||
command. It accepts the optional ``returncode`` argument to customize the exit
|
||
status for the management command to exit with, using :func:`sys.exit`.
|
||
|
||
If a management command is called from code through
|
||
:func:`~django.core.management.call_command`, it's up to you to catch the
|
||
exception when needed.
|