123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- ==============================================
- 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.
|